GCC 优化 std::tie 是否仅用于可读性?
Does GCC optimize std::tie used only for readability?
假设我有一个 std::tuple
:
std::tuple<int,int,int,int> t = {1,2,3,4};
我想使用 std::tie
只是为了提高可读性:
int a, b, c, d; // in real context these names would be meaningful
std::tie(a, b, c, d) = t;
对比仅使用 t.get<int>(0)
,等等
GCC 会优化此元组的内存使用还是会为 a, b, c, d
变量分配额外的 space?
在这种情况下,我看不出有任何理由不这样做,在 as-if rule the compiler only has to emulate the observable behavior of the program. A quick experiment using godbolt:
#include <tuple>
#include <cstdio>
void func( int x1, int x2,int x3, int x4)
{
std::tuple<int,int,int,int> t{x1,x2,x3,x4};
int a, b, c, d; // in real context these names would be meaningful
std::tie(a, b, c, d) = t;
printf( "%d %d %d %d\n", a, b, c, d ) ;
}
表明 gcc 确实优化了它:
func(int, int, int, int):
movl %ecx, %r8d
xorl %eax, %eax
movl %edx, %ecx
movl %esi, %edx
movl %edi, %esi
movl $.LC0, %edi
jmp printf
另一方面,如果您使用 t
地址并将其打印出来,我们现在可以观察到依赖 t
现有的行为(see it live ):
printf( "%p\n", static_cast<void*>(&t) );
我们可以看到 gcc 不再优化 t
:
movl %esi, 12(%rsp)
leaq 16(%rsp), %rsi
movd 12(%rsp), %xmm1
movl %edi, 12(%rsp)
movl $.LC0, %edi
movd 12(%rsp), %xmm2
movl %ecx, 12(%rsp)
movd 12(%rsp), %xmm0
movl %edx, 12(%rsp)
movd 12(%rsp), %xmm3
punpckldq %xmm2, %xmm1
punpckldq %xmm3, %xmm0
punpcklqdq %xmm1, %xmm0
归根结底,您需要查看编译器生成的内容并分析您的代码,在更复杂的情况下,这可能会让您大吃一惊。仅仅因为允许编译器进行某些优化并不意味着它会。我已经研究过更复杂的情况,其中编译器没有按照我对 std::tuple
的预期进行操作。 godbolt 在这里是一个非常有用的工具,我无法计算我过去有多少优化假设是通过将简单示例插入 godbolt 来推翻的。
请注意,我通常在这些示例中使用 printf
,因为 iostreams 会生成大量妨碍示例运行的代码。
假设我有一个 std::tuple
:
std::tuple<int,int,int,int> t = {1,2,3,4};
我想使用 std::tie
只是为了提高可读性:
int a, b, c, d; // in real context these names would be meaningful
std::tie(a, b, c, d) = t;
对比仅使用 t.get<int>(0)
,等等
GCC 会优化此元组的内存使用还是会为 a, b, c, d
变量分配额外的 space?
在这种情况下,我看不出有任何理由不这样做,在 as-if rule the compiler only has to emulate the observable behavior of the program. A quick experiment using godbolt:
#include <tuple>
#include <cstdio>
void func( int x1, int x2,int x3, int x4)
{
std::tuple<int,int,int,int> t{x1,x2,x3,x4};
int a, b, c, d; // in real context these names would be meaningful
std::tie(a, b, c, d) = t;
printf( "%d %d %d %d\n", a, b, c, d ) ;
}
表明 gcc 确实优化了它:
func(int, int, int, int):
movl %ecx, %r8d
xorl %eax, %eax
movl %edx, %ecx
movl %esi, %edx
movl %edi, %esi
movl $.LC0, %edi
jmp printf
另一方面,如果您使用 t
地址并将其打印出来,我们现在可以观察到依赖 t
现有的行为(see it live ):
printf( "%p\n", static_cast<void*>(&t) );
我们可以看到 gcc 不再优化 t
:
movl %esi, 12(%rsp)
leaq 16(%rsp), %rsi
movd 12(%rsp), %xmm1
movl %edi, 12(%rsp)
movl $.LC0, %edi
movd 12(%rsp), %xmm2
movl %ecx, 12(%rsp)
movd 12(%rsp), %xmm0
movl %edx, 12(%rsp)
movd 12(%rsp), %xmm3
punpckldq %xmm2, %xmm1
punpckldq %xmm3, %xmm0
punpcklqdq %xmm1, %xmm0
归根结底,您需要查看编译器生成的内容并分析您的代码,在更复杂的情况下,这可能会让您大吃一惊。仅仅因为允许编译器进行某些优化并不意味着它会。我已经研究过更复杂的情况,其中编译器没有按照我对 std::tuple
的预期进行操作。 godbolt 在这里是一个非常有用的工具,我无法计算我过去有多少优化假设是通过将简单示例插入 godbolt 来推翻的。
请注意,我通常在这些示例中使用 printf
,因为 iostreams 会生成大量妨碍示例运行的代码。