use*_*108 4 c c++ optimization performance simd
过去使用旧浮点数的技巧永远不会乘以2,而是添加一个自身的操作数,如2*a = a + a.今天使用SSE/SSE2/SSSE3/NEON/...指令集等的旧技巧仍然可行吗?我的操作数将是一个向量(例如,4个浮点数,我想乘以2).那么乘以3,4 ......?
编译器编写者很聪明.对于浮点数x,2.0*x和x + x绝对相同.因此,编译器能够用x + x替换2.0*x,反之亦然,具体取决于速度更快.
这可能很复杂.增加通常更快.但考虑一个处理器,它可以说每个周期一个乘法和一个加法.然后你想用2*x和y + y代替2*x和2*y.如果你有2*x和y + z的操作,那么你不想用x + x替换2*x,因为你有两个加法,你只能在两个周期内完成.然后存在具有融合乘法加法的处理器,其可以在一次操作中计算a*b + c.因此,您不希望将2*x + y更改为(x + x)+ y.
最好留给编译器.
我仍然试图找到一个可以产生影响的例子.我的直觉是,如果延迟是一个问题,有些情况x+x会更好,但如果延迟不是问题,只有吞吐量很重要,那么可能会更糟.但首先让我们讨论一些硬件.
让我坚持使用英特尔x86处理器,因为这是我最了解的.让我们考虑以下几代硬件:Core2/Nehalem,SandyBridge/IvyBridge和Haswell/Broadwell.
SIMD浮点指针算术运算的延迟和吞吐量:
这是我实际用于生成Mandelbrot集合的情况,该集合的因子为2.在主循环中,两个最关键的代码行是:
x = x*x - y*y + x0;
y = 2*xtemp*y + y0;
Run Code Online (Sandbox Code Playgroud)
这里的所有变量都是SIMD(SSE或AVX)寄存器,所以我一次作用于多个像素(4个用SSE,8个用AVX用于单个浮点).我正在使用围绕内在函数的SIMD类来实现此目的.因为y我可以做
y = xtemp*y + xtemp*y + y0
Run Code Online (Sandbox Code Playgroud)
用FMA怎么样?
y = fma(2*xtemp, y, y0)
Run Code Online (Sandbox Code Playgroud)
要么
y = xtemp*y + fma(xtemp, y, y0);
Run Code Online (Sandbox Code Playgroud)
可以尝试许多变化.我没试过,y=xtemp*y + xtemp*y + y0但我认为情况会更糟.顺便提一下,FMA结果,到目前为止我在Haswell系统上实现它的方式,并没有多大帮助.我的帧速率仅使用FMA增加了15%左右,而当我使用带有SSE的4个像素到使用AVX的8个像素时,它几乎翻倍.
编辑:这里有一些案例,我虽然会有所作为,但要么他们不在实践中,要么他们没有意义.
考虑这种情况
for(int i=0; i<n; i++) y[i] = 2*x[i];
Run Code Online (Sandbox Code Playgroud)
在这种情况下,延迟无关紧要,吞吐量很重要.在Haswell和Broadwell上,乘法的吞吐量是两次加法,因此在这种情况下可能看起来更糟糕x+x但是由于Haswell/Broadwell每个时钟周期只能写32字节,所以它没有什么区别.
这是一个使用x+x似乎更好的情况.
for(int i=0; i<n; i++) prod = prod * (2*x[i]);
Run Code Online (Sandbox Code Playgroud)
相反,你可以这样做:
for(int i=0; i<n; i++) prod = prod * (x[i]+x[i]);
Run Code Online (Sandbox Code Playgroud)
在这两种情况下它都没有区别,因为它们受到乘法延迟的支配prod.但是,如果您将循环展开足够的次数以使延迟无关紧要,那么第二种情况通常会更好,因为所有处理器都可以至少在每个时钟周期进行加法和乘法.虽然Haswell和Broadwell可以在每个时钟周期进行两次乘法,但它们也可以使用FMA在每个时钟周期进行两次乘法和加法,因此即使在它们上也会更好.
但是,在这种情况下,聪明的事情是
for(int i=0; i<n; i++) prod *= x[i];
prod *= pow(2,n);
Run Code Online (Sandbox Code Playgroud)
所以没有必要去x+x代替2*x.
| 归档时间: |
|
| 查看次数: |
335 次 |
| 最近记录: |