我有以下代码:
for(uint i=0; i<6; i++)
coeffs[i] = coeffs[i] < 0 ? 1.f/0.f : coeffs[i];
使用6个元素检查数组,如果找到负数条目,则将其设置为无穷大,否则保持条目不变。
我需要在不使用任何if语句的情况下做同样的事情
答案 0 :(得分:2)
当输入小于0时,一个显而易见的问题是你需要什么。
如果结果可能是负无穷大,我会这样做:
coeffs[i] /= (coeffs[i] >= 0.0);
如果输入为正,则coeffs[i] >= 0.0
产生1.0,如果输入为负,则0.0
产生coeffs[i] = (fabs(coeffs[i]) / (coeffs[i] >= 0.0);
。将输入除以1.0会使其保持不变。将其除以0会产生无穷大。
如果必须是正无穷大,您可以将其更改为:
fabs
通过取除除法之前的绝对值,我们为负数产生的无穷大被迫为正。否则,输入开始为正,因此#include <limits>
double f(double in) {
return in / (in >= 0.0);
}
double g(double in) {
return in > 0.0 ? in : std::numeric_limits<double>::infinity();
}
和除以1.0会使值保持不变。
至于这是否会真正提高性能,这可能会引发更多问题。目前,让我们看看CPU的代码,因为Godbolt让我们可以很容易地检查它。
如果我们看一下:
xorpd xmm1, xmm1
cmplesd xmm1, xmm0
movsd xmm2, qword ptr [rip + .LCPI0_0] # xmm2 = mem[0],zero
andpd xmm2, xmm1
divsd xmm0, xmm2
ret
因此,让我们看看为第一个函数生成的代码:
xorpd xmm1, xmm1
cmpltsd xmm1, xmm0
andpd xmm0, xmm1
movsd xmm2, qword ptr [rip + .LCPI1_0] # xmm2 = mem[0],zero
andnpd xmm1, xmm2
orpd xmm0, xmm1
ret
所以这并不是太糟糕 - 无分支,并且(取决于所涉及的确切处理器)在大多数合理的现代处理器上的吞吐量大约 8-10个周期。另一方面,这是为第二个函数生成的代码:
<div>
这也是无分支的 - 并且没有那个(相对慢的)divsd指令。同样,性能将根据具体的处理器而有所不同,但我们可能会计划大约6个周期左右的吞吐量 - 而不是非常比以前更快,但可能至少快几个周期部分时间,几乎肯定永远不会慢。简而言之,它几乎可以在任何可能的CPU下使用。
答案 1 :(得分:1)
预先发布大免责声明:我实际上没有对此进行测试,但我怀疑它确实比使用三元组更快。执行基准测试以确定它是否真的是一种优化!
另外:这些是在C中实现/测试的。它们应该可以轻松移植到GLSL,但是你可能需要明确的类型转换,这可能会使它们(甚至)变慢。
根据您是否严格需要INFINITY
或只使用较大的值,有两种方法可以执行此操作。既不使用分支表达式或语句,但它们确实涉及比较。两者都使用C中的比较运算符总是返回0
或1
。
基于INFINITY
的方式使用2元素数组并且比较输出选择choice-array的元素:
float chooseCoefs[2] = {0.f, INFINITY}; /* initialize choice-array */
for(uint i = 0; i < 6; i++){
int neg = coefs[i] < 0; /* outputs 1 or 0 */
/* set 0-element of choice-array to regular value */
chooseCoefs[0] = coefs[i];
/* if neg == 0: pick coefs[i], else neg == 1: pick INFINITY */
coefs[i] = chooseCoefs[neg];
}
如果你可以使用普通(但很大)的值而不是INFINITY
,你可以使用两次乘法&amp;一个补充:
#define BIGFLOAT 1000.f /* a swimming sasquatch... */
for(uint i = 0; i < 6; i++){
int neg = coefs[i] < 0;
/* if neg == 1: 1 * BIGFLOAT + 0 * coefs[i] == BIGFLOAT,
else neg == 0: 0 * BIGFLOAT + 1 * coefs[i] == coefs[i] */
coefs[i] = neg * BIGFLOAT + !neg * coefs[i];
}
同样,我没有对这些进行基准测试,但我的猜测是,至少基于阵列的解决方案比简单的三元组慢得多。不要低估编译器的优化能力!