我需要快速否定大量的 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