这些关于GCC优化的评论是否有效?

时间:2013-02-26 15:44:59

标签: c++ c optimization gcc

Demystifying the Restrict Keyword的底部是这个好奇的建议:

  

由于在GCC中完成调度的顺序,简化表达式总是更好。 不要将内存访问与计算混合在一起。代码可以按如下方式重写:

然后有一个基本上改变这个

的例子
velocity_x[i] += acceleration_x[i] * time_step;

进入这个

const float ax  = acceleration_x[i];       // Then the same follows for y, z
const float vx  = velocity_x[i];           // etc for y, z
const float nvx = vx + ( ax * time_step ); // etc
velocity_x[i]   = nvx;                     // ...

真的?我认为这种转换是琐碎的与优化编译器必须做的其他事情相比,例如std::foreach的lambda参数等等。

这只是陈旧,愚蠢的建议吗?或者,GCC不能或不会这样做有充分的理由吗? (这让我担心使用我的velocity += acceleration * time_step课程将上述内容写成Vector3f

2 个答案:

答案 0 :(得分:15)

修改 (我正在删除有关restrict的详细信息,因为它与实际提出的问题不符并引起混淆.OP假定restict使用。)

对于优化编译器,您的问题中的转换确实微不足道,但这不是Acton的论文所暗示的。

以下是the paper中完成的转化:

此代码......

  for (size_t i=0;i<count*stride;i+=stride)
  {
    velocity_x[i] += acceleration_x[i] * time_step;
    velocity_y[i] += acceleration_y[i] * time_step;
    velocity_z[i] += acceleration_z[i] * time_step;
    position_x[i] += velocity_x[i]     * time_step;
    position_y[i] += velocity_y[i]     * time_step;
    position_z[i] += velocity_z[i]     * time_step;
  }

...转换为此代码:

  for (size_t i=0;i<count*stride;i+=stride)
  {
    const float ax  = acceleration_x[i];
    const float ay  = acceleration_y[i];
    const float az  = acceleration_z[i];
    const float vx  = velocity_x[i];
    const float vy  = velocity_y[i];
    const float vz  = velocity_z[i];
    const float px  = position_x[i];
    const float py  = position_y[i];
    const float pz  = position_z[i];

    const float nvx = vx + ( ax * time_step );
    const float nvy = vy + ( ay * time_step );
    const float nvz = vz + ( az * time_step );
    const float npx = px + ( vx * time_step );
    const float npy = py + ( vy * time_step );
    const float npz = pz + ( vz * time_step );

    velocity_x[i]   = nvx;
    velocity_y[i]   = nvy;
    velocity_z[i]   = nvz;
    position_x[i]   = npx;
    position_y[i]   = npy;
    position_z[i]   = npz;
  }

什么是优化?

优化 - 如建议的那样 - 将1个表达式分离为3个表达式。

优化是在操作任何特定数据的指令之间插入有用的指令

如果您按照从velocity_x[i]vx再到nvx的数据回到velocity_x[i],则CPU会在每个步骤之间执行其他操作。

为什么这是优化?

现代CPU通常有pipelined architecture

由于指令是分阶段执行的,因此CPU允许同时处理多条指令。但是,当指令需要另一个尚未完全执行的指令的结果时,该管道将停止。在停止的指令可以运行之前,不会执行进一步的指令。

为什么我的优化编译器不会自动执行此操作?

有些人这样做。

GCC在这种优化方面表现得相对较差。

我使用gcc 4.7(x86-64架构,-O3优化)对上面的两个循环进行了反汇编。生成了类似的程序集,但指令的顺序不同,第一个版本产生了显着的停顿,其中一个浮点数将被加载,更改并存储在几条指令的范围内。

您可以阅读一些关于gcc的指令调度here,或者只是在网上搜索 gcc指令调度,看看很多关于这个问题的沮丧文章。

答案 1 :(得分:1)

在我看来,陈旧/愚蠢的建议。我的意思是详细程度是特定于编译器,编译器版本,处理器,处理器版本等。我坚持可读性并让编译器完成它的工作。如果有人担心可能的时钟周期或某个目标中的两个时钟周期,请为该目标编写一些#def程序集,并将更高级别的代码留给其他目标和参考。