我被告知代码中的分支
int value = //some number;
if(value > some_other_value)
value *= 23;
else
value -= 5;
可以通过按位屏蔽消除(为了对代码启用SIMD优化):
const int Mask = (some_other_value-value)>>31;
value = ((value * 23)&Mask)|((value-5)&~Mask);
但是,我不明白它是如何工作的(尽管我理解这里使用的是什么操作以及结果在二进制中看起来如何)。此外,这一般如何适用?如果原始代码改为
,该怎么办?if(value & 1 == 1)
value *= 23;
else
value -= 5;
分支删除代码是否仍然相同?否则,面具的目的是什么,我应该如何创建呢?这里发生了什么?
答案 0 :(得分:4)
这有效:
const int Mask = (some_other_value-value)>>31;
value = ((value * 23)&Mask)|((value-5)&~Mask);
掩码成为some_other_value - value
的符号位 - 类似于:
if (value > some_other_value) mask = -1; else mask = 0;
你可以使用第二个例子来实现同样的目的:
mask = -(value & 1);
所以,-0 = 0,-1 =全部。
编辑:我还要记住,如果计算过于复杂,那么你在分支版本上没有获得任何东西,特别是如果分支是合理可预测的。
答案 1 :(得分:0)
在最好的情况下这是一个过早的优化,在最坏的情况下是反优化。
如果代码可以进行矢量化,它将使用条件移动,因为SIMD不知道其他任何内容。
但是即使对于标量代码,现代编译器通常会生成条件移动,因此没有分支(除非编译器认为评估两个方程都足够昂贵,因此分支更有效。)
条件移动几乎是RISC处理器(例如ARM)的标准功能,甚至在x86上也支持了大约17年。在现代处理器上,条件移动将采用与正常移动完全相同的周期数,或者最多可能需要2-3个周期。
这显然假设条件得到足够早的评估(尽管如果不依赖于值并不重要,因为无序执行会隐藏它),但是任何类型的神秘优化黑客都是这样的情况申请也是。你只是不能使用那里还没有的结果。
如果你可以提供帮助,请始终编写易于理解的代码初看,而不是一些混淆
value = (((foo<<31)&bar, ++baz) -= (foo & 7121)) + PHASE_OF_MOON;
有些东西,它们不仅没有更快,而且可能更慢,而且还会让某些人在审核你的代码时感到困惑(包括你自己,在6-10个月后!),非常高便携式,并且很可能在你没有预料到的情况下产生不正确的结果。