变量引用(别名)会导致运行时成本吗?

发布于 2024-08-28 13:11:42 字数 364 浏览 5 评论 0原文

也许这是编译器特定的事情。如果是这样,那么 gcc (g++) 怎么样?如果您使用这样的变量引用/别名:

int x = 5;
int& y = x;
y += 10;

它实际上是否比我们不使用引用需要更多的周期。

int x = 5;
x += 10;

换句话说,机器代码是否发生变化,或者“别名”仅发生在编译器级别?

这似乎是一个愚蠢的问题,但我很好奇。特别是在暂时重命名一些成员变量以便数学代码更容易阅读的情况下可能会很方便。当然,我们并不是在这里谈论瓶颈……但这是我正在做的事情,所以我只是想知道是否存在任何“实际”差异……或者只是表面上的差异。

Maybe this is a compiler specific thing. If so, how about for gcc (g++)? If you use a variable reference/alias like this:

int x = 5;
int& y = x;
y += 10;

Does it actually require more cycles than if we didn't use the reference.

int x = 5;
x += 10;

In other words, does the machine code change, or does the "alias" happen only at the compiler level?

This may seem like a dumb question, but I am curious. Especially in the case where maybe it would be convenient to temporarily rename some member variables just so that the math code is a little easier to read. Sure, we're not exactly talking about a bottleneck here... but it's something that I'm doing and so I'm just wondering if there is any 'actual' difference... or if it's only cosmetic.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

晒暮凉 2024-09-04 13:11:42

可以将其视为别名,但从效率角度来看则不然。在底层,引用是一个具有更好语法和更高安全保证的指针。因此,您会受到“取消引用”操作运行时的惩罚。除非编译器优化它,但我通常不会指望这一点。

如果问编译器是否会对其进行优化,除了查看生成的程序集之外没有其他方法。

It may be treated as an alias, but not in terms of efficiency. Underneath the hood, a reference is a pointer with a nicer syntax and higher safety guarantees. Hence you've got a "dereference" operation runtime penalty. UNLESS the compiler optimizes it, but I wouldn't count on that usually.

In case of the question of whether the compiler will optimize it or not, there's no other way than to look at the generated assembly.

何必那么矫情 2024-09-04 13:11:42

确实,在大多数情况下,引用实现了“别名”的概念,即它们所绑定的对象的替代名称。

然而,一般情况下引用是通过指针实现的。然而,一个好的编译器只会在运行时确定实际绑定的情况下使用实际指针来实现引用。如果绑定在编译时已知(并且类型匹配),则编译器通常会将引用实现为同一对象的替代名称,在这种情况下,通过引用访问对象不会造成性能损失(与访问对象相比)它通过其原始名称)。

您的示例是您应该期望参考不会造成性能损失的示例之一。

It is true that in most cases references implement the concept of "alias", an alternative name for the object they are bound to.

However, in general case references are implemented through pointers. Nevertheless, a good compiler will only use an actual pointer to implement reference in situations when the actual binding is determined at run-time. If the binding is known at compile time (and the types match), the compiler will normally implement reference as an alternative name for the same object, in which case there will be no performance penalty for accessing the object through the reference (compared to accessing it through its original name).

Your example is one of those when you should expect no performance penalty from the reference.

明天过后 2024-09-04 13:11:42

我比较了 Gnu/Linux 上的 2 个程序。下面仅显示了 GCC 输出,但 clang 结果得出了相同的结论。

GCC 版本: 4.9.2

Clang 版本: 3.4.2

程序

1.cpp

#include <stdio.h>
int main()
{
    int x = 3;
    printf("%d\n", x);
    return 0;
}

2.cpp

#include <stdio.h>
int main()
{
    int x = 3;
    int & y = x;
    printf("%d\n", y);
    return 0;
}

测试

尝试 1:无优化

gcc -S --std=c++11 1.cpp

gcc -S --std=c++11 2.cpp

1.cpp 生成的程序集较短。

尝试2:优化

gcc -S -O2 --std=c++11 1.cpp

gcc -S -O2 --std=c++ 11 2.cpp

生成的程序集完全相同。

汇编输出

1.cpp,无优化

    .file   "1.cpp"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $3, -4(%rbp)
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

2.cpp,无优化

    .file   "2.cpp"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $3, -12(%rbp)
    leaq    -12(%rbp), %rax
    movq    %rax, -8(%rbp)
    movq    -8(%rbp), %rax
    movl    (%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

1.cpp,有优化

    .file   "1.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

2.cpp,有优化

    .file   "1.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

结论

对于优化的 GCC 输出来说,没有运行时成本。 clang 也是如此(使用版本 3.4.2 进行测试):当优化打开时,两个程序中生成的汇编代码是相同的。

I compared 2 programs on Gnu/Linux. Only GCC output is shown below, but clang results lead to identical conclusions.

GCC version: 4.9.2

Clang version: 3.4.2

The programs

1.cpp

#include <stdio.h>
int main()
{
    int x = 3;
    printf("%d\n", x);
    return 0;
}

2.cpp

#include <stdio.h>
int main()
{
    int x = 3;
    int & y = x;
    printf("%d\n", y);
    return 0;
}

The test

Attempt 1: No optimizations

gcc -S --std=c++11 1.cpp

gcc -S --std=c++11 2.cpp

1.cpp's resulting assembly was shorter.

Attempt 2: Optimizations on

gcc -S -O2 --std=c++11 1.cpp

gcc -S -O2 --std=c++11 2.cpp

The resulting assembly was completely identical.

The assembly output

1.cpp, no optimization

    .file   "1.cpp"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $3, -4(%rbp)
    movl    -4(%rbp), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

2.cpp, no optimization

    .file   "2.cpp"
    .section    .rodata
.LC0:
    .string "%d\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $16, %rsp
    movl    $3, -12(%rbp)
    leaq    -12(%rbp), %rax
    movq    %rax, -8(%rbp)
    movq    -8(%rbp), %rax
    movl    (%rax), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

1.cpp, with optimizations

    .file   "1.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

2.cpp, with optimizations

    .file   "1.cpp"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d\n"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $3, %esi
    movl    $.LC0, %edi
    xorl    %eax, %eax
    call    printf
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE12:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
    .section    .note.GNU-stack,"",@progbits

Conclusion

There is no runtime cost when it comes to optimized GCC output. Same goes with clang (tested with version 3.4.2): when optimizations are on, the generated assembly code is identical in both programs.

心欲静而疯不止 2024-09-04 13:11:42

唯一确定的方法是编译它并检查编译器的输出。一般来说,引用的开销与指针的开销类似,因为指针通常是引用的实现方式。但是,考虑到您所展示的简单案例,我相信该参考将被优化掉。

The only way to know for sure is to compile it and examine the output of the compiler. Generally, the overhead for a reference is similar to the overhead for a pointer, because pointers are usually how references are implemented. However, given the simple case you're showing, I believe the reference would be optimized away.

奢华的一滴泪 2024-09-04 13:11:42

这两个函数在 g++ 中编译为完全相同的代码,甚至仅使用 -O1 也是如此。 (我添加了 return 语句以确保计算不会完全消除。)

没有指针,只有引用。在这个简单的例子中,没有性能差异。但这并不能保证对于所有引用的使用总是如此(没有性能差异)。

int f()
{
    int x = 5;
    x += 10;
    return x;
}

int f()
{
    int x = 5;
    int & y = x;
    y += 10;
    return y;
}

汇编器:

movl    $15, %eax
ret

Both of these functions compile to exactly the same code in g++, even just using -O1. (I added the return statement to ensure that the calculation wasn't eliminated entirely.)

There is no pointer, only a reference. In this trivial example there was no performance difference. That's no guarantee that this would always be the case (no performance difference) for all uses of reference, though.

int f()
{
    int x = 5;
    x += 10;
    return x;
}

.

int f()
{
    int x = 5;
    int & y = x;
    y += 10;
    return y;
}

Assembler:

movl    $15, %eax
ret
伏妖词 2024-09-04 13:11:42

是的,取消引用引用后面的指针会产生额外的运行时成本,但可能微不足道。以最清晰易读、最清楚地表达您想要的语义的方式编写代码,然后在性能成为问题时在分析器中运行(瓶颈很少是您猜测的那样)。如果您使用的是 MacOS,Shark 非常棒。

Yes, dereferencing the pointer behind the reference incurs additional runtime costs, but is likely insignificant. Write the code in whatever way is clearest to read and most cleanly articulates the semantics you are aiming for, and then run in a profiler if performance is an issue (the bottleneck is rarely what you guess it is). If you are on MacOS, Shark is fantastic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文