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有自己的指令集——但考虑到它们因分支而受到的惩罚,它们的编译器(以及