Performance 如何在不使用if语句(或三元)的情况下将负数设置为无穷大
我有以下代码:Performance 如何在不使用if语句(或三元)的情况下将负数设置为无穷大,performance,if-statement,opengl,glsl,ternary-operator,Performance,If Statement,Opengl,Glsl,Ternary Operator,我有以下代码: for(uint i=0; i<6; i++) coeffs[i] = coeffs[i] < 0 ? 1.f/0.f : coeffs[i]; for(uint i=0;i预先声明:我还没有实际测试过这个,但我怀疑它真的比使用ternaries快。执行基准测试,看看它是否真的是一个优化! 另外:这些都是用C实现/测试的。它们应该很容易移植到GLSL,但是您可能需要显式类型转换,这可能会使它们(甚至)更慢 有两种方法可以实现这一点,具体取决于您是否严
for(uint i=0; i<6; i++)
coeffs[i] = coeffs[i] < 0 ? 1.f/0.f : coeffs[i];
for(uint i=0;i预先声明:我还没有实际测试过这个,但我怀疑它真的比使用ternaries快。执行基准测试,看看它是否真的是一个优化!
另外:这些都是用C实现/测试的。它们应该很容易移植到GLSL,但是您可能需要显式类型转换,这可能会使它们(甚至)更慢
有两种方法可以实现这一点,具体取决于您是否严格需要无穷大
,或者是否可以只使用一个大值。这两种方法都不使用分支表达式或语句,但它们都涉及比较。这两种方法都使用C中的比较运算符总是返回0
或1
基于无限
的方法使用2元素数组,并让比较输出选择选择数组中的元素:
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];
}
同样,我没有对这些进行基准测试,但我的猜测是,至少基于数组的解决方案比简单的ternaries慢得多。不要低估编译器的优化能力!前面有一条重要的免责声明:我还没有实际测试过这一点,但我怀疑它是否真的比使用ternaries快。执行基准测试,看看它是否真的有效y是一种优化!
另外:这些都是用C实现/测试的。它们应该很容易移植到GLSL,但是您可能需要显式类型转换,这可能会使它们(甚至)更慢
有两种方法可以实现这一点,具体取决于您是否严格需要无穷大
,或者是否可以只使用一个大值。这两种方法都不使用分支表达式或语句,但它们都涉及比较。这两种方法都使用C中的比较运算符总是返回0
或1
基于无限
的方法使用2元素数组,并让比较输出选择选择数组中的元素:
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];
}
同样,我没有对这些进行基准测试,但我的猜测是,至少基于数组的解决方案比简单的三元组慢得多。不要低估编译器的优化能力!一个明显的问题是,当输入小于0时,需要什么样的无穷大
任意无穷大
如果结果可以是负无穷大,我会这样做:
coeffs[i] /= (coeffs[i] >= 0.0);
如果输入为正,coefs[i]>=0.0
产生1.0,如果输入为负,0.0
产生1.0。将输入除以1.0保持不变。将输入除以0产生无穷大
正无穷大
如果它必须是一个正无穷大,你可以将它改为:
coeffs[i] = (fabs(coeffs[i]) / (coeffs[i] >= 0.0);
通过在除法之前取绝对值,我们为负数生成的无穷大被强制为正。否则,输入开始为正,因此fabs
和1.0除法保持值不变
演出
至于这是否真的会提高性能,这可能还有更多的问题。现在,让我们看看CPU的代码,因为Godbolt让我们很容易地检查它
如果我们看看这个:
#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();
}
所以这并不可怕——无分支,并且(取决于所涉及的确切处理器)在大多数合理的现代处理器上的吞吐量大约为8-10个周期。另一方面,下面是为第二个函数生成的代码:
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
这也是无分支的——并且没有分支(相对较慢)divsd指令也是如此。同样,性能会因具体处理器的不同而有所不同,但我们可能会计划将吞吐量控制在6个周期左右——不会比前一个指令快很多,但可能会在一段时间内至少快几个周期,而且几乎肯定不会再慢。简言之,这可能更可取在几乎任何可能的CPU下
GPU代码
当然,GPU有自己的指令集——但考虑到它们因分支而受到的惩罚,它们的编译器(以及它们提供的指令集)在消除分支方面的作用可能至少与CPU一样多,因此简单的代码也很有可能在其上正常工作(虽然可以肯定地说,您需要检查它生成的代码或对其进行分析)。一个明显的问题是,当输入小于0时,您需要什么无穷大
任意无穷大
如果结果可以是负无穷大,我会这样做:
coeffs[i] /= (coeffs[i] >= 0.0);
如果输入为正,coefs[i]>=0.0
产生1.0,如果输入为负,0.0
产生1.0。将输入除以1.0保持不变。将输入除以0产生无穷大
正无穷大
如果它必须是一个正无穷大,你可以将它改为:
coeffs[i] = (fabs(coeffs[i]) / (coeffs[i] >= 0.0);
通过在除法之前取绝对值,我们为负数生成的无穷大被强制为正。否则,输入开始为正,因此fabs
和1.0除法保持值不变
演出
至于这是否真的会提高性能,这可能还有更多的问题。现在,让我们看看CPU的代码,因为Godbolt让我们很容易地检查它
如果我们看看这个:
#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();
}
所以这并不可怕——无分支,并且(取决于所涉及的确切处理器)在大多数合理的现代处理器上的吞吐量大约为8-10个周期。另一方面,下面是为第二个函数生成的代码:
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
这也是无分支的——并且没有分支(相对较慢)divsd指令也是如此。同样,性能会因具体处理器的不同而有所不同,但我们可能会计划将吞吐量控制在6个周期左右——不会比前一个指令快很多,但可能会在一段时间内至少快几个周期,而且几乎肯定不会再慢。简言之,这可能更可取在几乎任何可能的CPU下
GPU代码
当然,GPU有自己的指令集——但考虑到它们因分支而受到的惩罚,它们的编译器(以及