使用旧浮标的技巧过去是永远不乘以2,而是自己添加一个操作数,例如,2*a = a +a。对于SSE/SSE 2/SSSE3 3/NEON/…今天的指令集之类的?我的操作数将是一个向量(例如,4浮点数,我想乘2)。乘以3,4怎么样.?
发布于 2014-08-29 09:20:32
我还在努力找一个例子来说明这会有什么不同。我的直觉是,如果延迟是一个问题,那么有些情况下x+x会更好,但是如果延迟不是问题,只有吞吐量才重要,那么情况可能会更糟。但首先让我们讨论一些硬件。
让我坚持英特尔的x86处理器,因为这是我最了解的。让我们考虑以下几代硬件:Core2 2/Nehalem、SandyBridge/IvyBridge和Haswell/Broadwell。
SIMD浮动指针算法操作的延迟和吞吐量:
,这里是我用来生成Mandelbrot集的一个例子,它的因子为2。在主循环中,最关键的代码行有两行:
x = x*x - y*y + x0;
y = 2*xtemp*y + y0;这里的所有变量都是SIMD (SSE或AVX)寄存器,因此我一次对多个像素进行操作(4与SSE,8与AVX用于单个浮点)。为此,我使用了一个SIMD类,它围绕着本质。对于y,我可以代替
y = xtemp*y + xtemp*y + y0那FMA呢?
y = fma(2*xtemp, y, y0)或
y = xtemp*y + fma(xtemp, y, y0);有许多变化是可以尝试的。我还没有试过y=xtemp*y + xtemp*y + y0,但我认为情况会更糟。顺便说一句,到目前为止,我在Haswell系统上实现FMA的方式并没有多大帮助。我的帧率只增加了15%左右使用FMA,而当我从使用4像素的SSE到8像素与AVX,它几乎加倍。
编辑:这里有一些我认为会有所改变的案例,但它们要么在实践中行不通,要么就没有意义。
考虑这个案子
for(int i=0; i<n; i++) y[i] = 2*x[i];在这种情况下,延迟并不重要,吞吐量很重要。在Haswell和Broadwell上,乘法的吞吐量是两倍加法,所以在这种情况下,做x+x可能会更糟,但是由于Haswell/Broadwell每个时钟周期只能写32字节,所以没有什么区别。
在这里,使用x+x似乎更好。
for(int i=0; i<n; i++) prod = prod * (2*x[i]);相反,你可以这样做:
for(int i=0; i<n; i++) prod = prod * (x[i]+x[i]);在这两种情况下,这将不会有任何区别,因为他们是主导的潜伏期的prod增殖。然而,如果你对循环展开足够多的时间,以至于延迟不重要,那么第二种情况通常会更好,因为所有处理器至少可以在每个时钟周期进行加法和乘法。虽然哈斯韦尔和布罗德威尔每个时钟周期可以进行两次乘法,但也可以使用FMA进行每个时钟周期的两次乘法和加法,因此即使在它们上,这也会更好。
然而,在这种情况下,明智的做法是
for(int i=0; i<n; i++) prod *= x[i];
prod *= pow(2,n);因此,没有必要执行x+x而不是2*x。
发布于 2014-08-28 19:52:28
编译器编写者很聪明。对于浮点数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。
最好把它留给编译器。
https://stackoverflow.com/questions/25556683
复制相似问题