我有这个C函数:
double f(int x)
{
if (x <= 0)
return 0.0;
else
return x * log(x);
}
我在一个紧密循环中调用它,并希望摆脱该分支以查看它是否提高了性能。
我不能使用它:
double f(int x)
{
return x * log(x);
}
因为它在x == 0
时返回NaN
(大约 25% 的时间是正确的)。
有没有另一种方法可以实现它,以便它在x == 0
时返回0
,但仍然摆脱分支?
(我不太关心负输入,因为这些是错误,而零不是。
首先注意 log(1) = 0。然后你可以把问题写成 x * log(y),其中 y = 1 如果 x <= 0,否则等于 x;如果 y = 1,则 x 无关紧要,因为 log(y)=0。
像 y = (x> 0)*x + (x <= 0) 这样的东西就可以做到这一点,然后:
double f(int x) {
return x * log((x > 0)*x + (x <= 0));
}
它只取决于 log(1) 和四个整数运算是否比一个分支差。
扩展可以在这里提供帮助。在 GCC 中,您将执行以下操作:
if(__builtin_expect(x > 0, 1)) {
return x * log(x);
}
return 0.0;
然后,GCC 将生成有利于x > 0 == 1
分支的机器代码。
如果你不关心负数,那么你可以将x == 0
视为一个不太可能的分支:
if(__builtin_expect(x == 0, 0)) {
return 0.0;
}
return x * log(x);
如果您不在 GCC 上,您应该检查编译器的文档,看看它是否提供了类似的功能。
请注意,它仍然不是无分支的。只是可能的分支花费的时间更少。
任何分支自由代码都必须包含x * log(x)
计算以涵盖"正常"情况。
因此,在尝试提出无分支代码之前,请单独测量x * log(x)
的速度。除非它比你拥有的代码快得多,否则这里没有什么重要的东西可以得到。我怀疑不会。