我需要快速否定大量的 double 。如果 bit_generator 生成0,则必须更改符号。如果 bit_generator 生成 1,则什么也不会发生。循环多次运行, bit_generator 非常快。在我的平台上,案例 2 明显快于案例 1。看起来我的 CPU 不喜欢分支。有没有更快、更便携的方法来做到这一点?你怎么看案例3?

// generates 0 and 1
int bit_generator();

// big vector (C++)
vector<double> v;

// case 1
for (size_t i=0; i<v.size(); ++i)
    if (bit_generator()==0)
        v[i] = -v[i];

// case 2
const int sign[] = {-1, 1};
for (size_t i=0; i<v.size(); ++i)
        v[i] *= sign[bit_generator()];

// case 3
const double sign[] = {-1, 1};
for (size_t i=0; i<v.size(); ++i)
        v[i] *= sign[bit_generator()];

// case 4 uses C-array
double a[N];
double number_generator(); // generates doubles
double z[2]; // used as buffer
for (size_t i=0; i<N; ++i) {
        z[0] = number_generator();
        z[1] = -z[0];
        a[i] = z[bit_generator()];
}

编辑: 添加了 case 4 和 C-tag,因为 vector 可以是普通数组。由于我可以控制 double 的生成方式,因此我重新设计了代码,如案例 4 所示。它同时避免了额外的乘法和分支。我认为它在所有平台上都应该很快。

最佳答案

除非你想在循环中调整 vector 的大小,否则将 v.size() 从 for 表达式中取出,即

const unsigned SZ=v.size();
for (size_t i=0; i<SZ; ++i)
    if (bit_generator()==0)
        v[i] = -v[i];

如果编译器看不到 bit_generator() 发生了什么,那么编译器可能很难证明 v.size() 没有改变,这使得循环展开或向量化变得不可能。

更新:我做了一些测试,在我的机器上方法 2 似乎是最快的。但是,使用一种我称之为“小组行动” :-)的模式似乎更快。基本上,您将多个决策组合成一个值并切换它:
const size_t SZ=v.size();
for (size_t i=0; i<SZ; i+=2) // manual loop unrolling
{
 int val=2*bit_generator()+bit_generator();
 switch(val) // only one conditional
 {
  case 0:
     break; // nothing happes
  case 1:
     v[i+1]=-v[i+1];
     break;
  case 2:
     v[i]=-v[i];
     break;
  case 3:
    v[i]=-v[i];
    v[i+1]=-v[i+1];
 }
}
// not shown: wrap up the loop if SZ%2==1

10-07 19:17
查看更多