4

我想知道编译器在优化结构分配方面做得如何,我做了一些测试,结果令人惊讶。(我想向 GCC 提出这个问题。)

特别是我正在查看 GCC 4.6.3 (-O3) 的汇编程序(编辑:我还包括 GCC 4.8.0 的输出)产生以下代码:

template<class T>
struct C
{
  T F[3];
  C(){}
  C(const T& t0,const T& t1,const T& t2): F{{t0},{t1},{t2}} {}
};

template<class T>
__attribute__((always_inline))
C<T>
operator+( const C<T>& l , const C<T>& r )
{
  return C<T>( l.F[0] + r.F[0] , 
           l.F[1] + r.F[1] , 
           l.F[2] + r.F[2] );
}

int main() 
{
  C<C<C<float> > > a,b,c;

  a=b+c;

  printf("%s",(const char*)&a);
}

这里是汇编程序:

movss   264(%rsp), %xmm0
leaq    48(%rsp), %rbx
leaq    156(%rsp), %rbp
addss   376(%rsp), %xmm0
movss   %xmm0, 4(%rsp)

movss   260(%rsp), %xmm0
addss   372(%rsp), %xmm0
movss   %xmm0, 8(%rsp)

movss   256(%rsp), %xmm0
addss   368(%rsp), %xmm0
movss   %xmm0, 12(%rsp)

movss   252(%rsp), %xmm0
addss   364(%rsp), %xmm0
movss   %xmm0, 16(%rsp)

movss   248(%rsp), %xmm0
addss   360(%rsp), %xmm0
movss   %xmm0, 20(%rsp)

movss   244(%rsp), %xmm0
addss   356(%rsp), %xmm0
movss   %xmm0, 24(%rsp)

movss   240(%rsp), %xmm0
addss   352(%rsp), %xmm0
movss   %xmm0, 28(%rsp)

movss   236(%rsp), %xmm0
addss   348(%rsp), %xmm0
movss   %xmm0, 32(%rsp)

movss   232(%rsp), %xmm0
addss   344(%rsp), %xmm0
movss   %xmm0, 36(%rsp)

movss   228(%rsp), %xmm0
addss   340(%rsp), %xmm0
movss   %xmm0, 40(%rsp)

movss   224(%rsp), %xmm0
addss   336(%rsp), %xmm0
movss   %xmm0, 44(%rsp)    // 11-th

movss   220(%rsp), %xmm0
movss   164(%rsp), %xmm14
movss   160(%rsp), %xmm15
addss   332(%rsp), %xmm0
addss   272(%rsp), %xmm15
movss   216(%rsp), %xmm1
addss   276(%rsp), %xmm14
movss   212(%rsp), %xmm2
movss   208(%rsp), %xmm3
addss   328(%rsp), %xmm1
movss   204(%rsp), %xmm4
addss   324(%rsp), %xmm2
movss   200(%rsp), %xmm5
addss   320(%rsp), %xmm3
movss   196(%rsp), %xmm6
addss   316(%rsp), %xmm4
movss   192(%rsp), %xmm7
addss   312(%rsp), %xmm5
movss   188(%rsp), %xmm8
addss   308(%rsp), %xmm6
movss   184(%rsp), %xmm9
addss   304(%rsp), %xmm7
movss   180(%rsp), %xmm10
addss   300(%rsp), %xmm8
movss   176(%rsp), %xmm11
addss   296(%rsp), %xmm9
movss   172(%rsp), %xmm12
addss   292(%rsp), %xmm10
movss   168(%rsp), %xmm13
addss   288(%rsp), %xmm11
addss   284(%rsp), %xmm12
movss   %xmm15, 384(%rsp)
addss   280(%rsp), %xmm13  // all 27 floats added
movss   %xmm14, 388(%rsp)
movss   %xmm0, 444(%rsp)
movss   44(%rsp), %xmm0
movss   %xmm0, 448(%rsp)
movss   40(%rsp), %xmm0
movss   %xmm0, 452(%rsp)
movss   36(%rsp), %xmm0
movss   %xmm0, 456(%rsp)
movss   32(%rsp), %xmm0
movss   %xmm0, 460(%rsp)
movss   28(%rsp), %xmm0
movss   %xmm0, 464(%rsp)
movss   24(%rsp), %xmm0
movss   %xmm0, 468(%rsp)
movss   20(%rsp), %xmm0
movss   %xmm0, 472(%rsp)
movss   16(%rsp), %xmm0
movss   %xmm0, 476(%rsp)
movss   12(%rsp), %xmm0
movss   %xmm0, 480(%rsp)
movss   8(%rsp), %xmm0
movss   %xmm13, 392(%rsp)
movss   %xmm12, 396(%rsp)
movss   %xmm11, 400(%rsp)
movss   %xmm10, 404(%rsp)
movss   %xmm9, 408(%rsp)
movss   %xmm8, 412(%rsp)
movss   %xmm7, 416(%rsp)
movss   %xmm6, 420(%rsp)
movss   %xmm5, 424(%rsp)
movss   %xmm4, 428(%rsp)
movss   %xmm3, 432(%rsp)
movss   %xmm2, 436(%rsp)
movss   %xmm1, 440(%rsp)
movss   %xmm0, 484(%rsp)  // Storing of temporary finished
movq    384(%rsp), %rax   // Now begin copy temporary to destination
movss   4(%rsp), %xmm0
movss   %xmm0, 488(%rsp)
movq    %rax, 48(%rsp)
movq    392(%rsp), %rax
movq    %rax, 56(%rsp)
movq    400(%rsp), %rax
movq    %rax, 64(%rsp)
movq    408(%rsp), %rax
movq    %rax, 72(%rsp)
movq    416(%rsp), %rax
movq    %rax, 80(%rsp)
movq    424(%rsp), %rax
movq    %rax, 88(%rsp)
movq    432(%rsp), %rax
movq    %rax, 96(%rsp)
movq    440(%rsp), %rax
movq    %rax, 104(%rsp)
movq    448(%rsp), %rax
movq    %rax, 112(%rsp)
movq    456(%rsp), %rax
movq    %rax, 120(%rsp)
movq    464(%rsp), %rax
movq    %rax, 128(%rsp)
movq    472(%rsp), %rax
movq    %rax, 136(%rsp)
movq    480(%rsp), %rax
movq    %rax, 144(%rsp)
movl    488(%rsp), %eax
movl    %eax, 152(%rsp)  // Whole struct copied
.p2align 4,,10

汇编器从指示的行开始显示整个(嵌套)结构的附加副本。有人可能会说这是因为编译器必须发出赋值(与可以省略的副本相反)。

我重复了这个实验,这次我只使用了 2 倍嵌套结构,而不是 3 倍嵌套结构。然后不会发生额外的副本。这是第二个实验的代码:

int main() 
{
  C<C<float> > a,b,c;

  a=b+c;

  printf("%s",(const char*)&a);
}

和汇编程序:

    movss   80(%rsp), %xmm0
    movq    %rsp, %rdx
    movss   76(%rsp), %xmm1
    addss   128(%rsp), %xmm0
    movss   72(%rsp), %xmm2
    addss   124(%rsp), %xmm1
    movss   68(%rsp), %xmm3
    addss   120(%rsp), %xmm2
    movss   64(%rsp), %xmm4
    addss   116(%rsp), %xmm3
    movss   60(%rsp), %xmm5
    addss   112(%rsp), %xmm4
    movss   56(%rsp), %xmm6
    addss   108(%rsp), %xmm5
    movss   52(%rsp), %xmm7
    addss   104(%rsp), %xmm6
    movss   48(%rsp), %xmm8
    addss   100(%rsp), %xmm7
    addss   96(%rsp), %xmm8
    xorl    %eax, %eax
    movss   %xmm2, 24(%rsp)
    movss   %xmm3, 20(%rsp)
    movss   %xmm4, 16(%rsp)
    movss   %xmm5, 12(%rsp)
    movss   %xmm6, 8(%rsp)
    movss   %xmm7, 4(%rsp)
    movss   %xmm8, (%rsp)
    movss   %xmm1, 28(%rsp)
    movss   %xmm0, 32(%rsp)

很明显,临时结构(保存 的结果b+c)可以完全分配在寄存器文件(9 个寄存器,xmm0-xmm8)中,随后无需复制即可存储。

然而,操作(加法+赋值)仅对存储容器上操作的指令造成弱数据依赖性。我要问的问题是:在第一个实验中,由于数据依赖性将允许指令的最佳调度,为什么编译器不以不需要额外副本的方式调度指令,即直接存储结果进入最终的内存地址?是否存在编译器无法查看的依赖性或边界的某些方面,从而有效地阻碍了这种优化?

编辑:

这里是从 GCC 4.8.0 (-std=c++0x -S -O3) 生成的汇编程序:

movss   264(%rsp), %xmm0
leaq    48(%rsp), %rdx
movss   260(%rsp), %xmm1
addss   376(%rsp), %xmm0
movss   256(%rsp), %xmm2
addss   372(%rsp), %xmm1
movss   252(%rsp), %xmm3
addss   368(%rsp), %xmm2
movss   248(%rsp), %xmm4
addss   364(%rsp), %xmm3
movss   244(%rsp), %xmm5
addss   360(%rsp), %xmm4
addss   356(%rsp), %xmm5
movss   196(%rsp), %xmm11
addss   308(%rsp), %xmm11
movss   %xmm0, 4(%rsp)
movss   %xmm1, 8(%rsp)
movss   %xmm2, 12(%rsp)
movss   %xmm3, 16(%rsp)
movss   %xmm4, 20(%rsp)
movss   %xmm5, 24(%rsp)
movss   240(%rsp), %xmm0
movss   236(%rsp), %xmm1
movss   232(%rsp), %xmm2
addss   352(%rsp), %xmm0
movss   228(%rsp), %xmm3
addss   348(%rsp), %xmm1
movss   224(%rsp), %xmm4
addss   344(%rsp), %xmm2
movss   220(%rsp), %xmm5
addss   340(%rsp), %xmm3
movss   216(%rsp), %xmm6
addss   336(%rsp), %xmm4
movss   212(%rsp), %xmm7
addss   332(%rsp), %xmm5
movss   208(%rsp), %xmm8
addss   328(%rsp), %xmm6
movss   204(%rsp), %xmm9
addss   324(%rsp), %xmm7
movss   200(%rsp), %xmm10
addss   320(%rsp), %xmm8
addss   316(%rsp), %xmm9
addss   312(%rsp), %xmm10
movss   %xmm11, 28(%rsp)
movss   192(%rsp), %xmm12
movss   188(%rsp), %xmm13
movss   184(%rsp), %xmm14
addss   304(%rsp), %xmm12
movss   180(%rsp), %xmm15
addss   300(%rsp), %xmm13
addss   296(%rsp), %xmm14
movss   176(%rsp), %xmm11
addss   292(%rsp), %xmm15
addss   288(%rsp), %xmm11
movss   %xmm12, 32(%rsp)
movss   %xmm13, 36(%rsp)
movss   %xmm14, 40(%rsp)
movss   %xmm15, 44(%rsp)
movss   172(%rsp), %xmm12
movss   168(%rsp), %xmm13
movss   164(%rsp), %xmm14
addss   284(%rsp), %xmm12
movss   160(%rsp), %xmm15
addss   280(%rsp), %xmm13
addss   276(%rsp), %xmm14
movss   %xmm11, 400(%rsp)
addss   272(%rsp), %xmm15
movss   %xmm12, 396(%rsp)
movss   %xmm13, 392(%rsp)
movss   %xmm14, 388(%rsp)
movss   36(%rsp), %xmm13
movss   40(%rsp), %xmm14
movss   %xmm15, 384(%rsp)
movss   32(%rsp), %xmm12
movss   44(%rsp), %xmm15
movss   %xmm15, 404(%rsp)
movss   %xmm14, 408(%rsp)
movss   %xmm13, 412(%rsp)
movss   %xmm12, 416(%rsp)
movq    384(%rsp), %rax
movss   28(%rsp), %xmm11
movss   %xmm11, 420(%rsp)
movq    %rax, 48(%rsp)
movq    392(%rsp), %rax
movss   %xmm5, 444(%rsp)
movss   %xmm4, 448(%rsp)
movss   24(%rsp), %xmm5
movq    %rax, 56(%rsp)
movss   %xmm3, 452(%rsp)
movq    400(%rsp), %rax
movss   20(%rsp), %xmm4
movss   16(%rsp), %xmm3
movss   %xmm2, 456(%rsp)
movq    %rax, 64(%rsp)
movq    408(%rsp), %rax
movss   %xmm1, 460(%rsp)
movss   12(%rsp), %xmm2
movss   8(%rsp), %xmm1
movq    %rax, 72(%rsp)
movq    416(%rsp), %rax
movss   %xmm0, 464(%rsp)
movss   4(%rsp), %xmm0
movss   %xmm10, 424(%rsp)
movss   %xmm9, 428(%rsp)
movss   %xmm8, 432(%rsp)
movss   %xmm7, 436(%rsp)
movss   %xmm6, 440(%rsp)
movss   %xmm5, 468(%rsp)
movss   %xmm4, 472(%rsp)
movss   %xmm3, 476(%rsp)
movss   %xmm2, 480(%rsp)
movss   %xmm1, 484(%rsp)
movss   %xmm0, 488(%rsp)
movq    %rax, 80(%rsp)
movq    424(%rsp), %rax
movq    %rax, 88(%rsp)
movq    432(%rsp), %rax
movq    %rax, 96(%rsp)
movq    440(%rsp), %rax
movq    %rax, 104(%rsp)
movq    448(%rsp), %rax
movq    %rax, 112(%rsp)
movq    456(%rsp), %rax
movq    %rax, 120(%rsp)
movq    464(%rsp), %rax
movq    %rax, 128(%rsp)
movq    472(%rsp), %rax
movq    %rax, 136(%rsp)
movq    480(%rsp), %rax
movq    %rax, 144(%rsp)
movl    488(%rsp), %eax
movl    %eax, 152(%rsp)

附加副本仍然存在。

4

0 回答 0