转换为无分支的连续if语句

时间:2015-02-15 10:56:02

标签: c if-statement optimization assembly

我在那里试图找出如何转换最后两个"如果"以下代码声明为无分支状态。

int u, x, y;
x = rand() % 100 - 50;
y = rand() % 100 - 50;

u = rand() % 4;
if ( y > x) u = 5;
if (-y > x) u = 4;

或者,如果上述情况太难,您可以将它们视为:

if (x > 0) u = 5;
if (y > 0) u = 4;

我认为让我这样的事实是那些没有else捕手的事实。如果是这种情况,我可能已经修改了无分支abs(或max / min)函数的变体。

您看到的rand()函数不属于实际代码。我这样添加它们只是为了暗示变量xyu在两个分支发生时可能具有的预期范围。

允许装配机器代码。

编辑:

经过一些braingrinding后,我设法组建了一个有效的无分支版本:

int u, x, y;
x = rand() % 100 - 50;
y = rand() % 100 - 50;

u = rand() % 4;
u += (4-u)*((unsigned int)(x+y) >> 31);
u += (5-u)*((unsigned int)(x-y) >> 31);

不幸的是,由于涉及整数运算,使用if语句的原始版本的速度提高了30%。

编译器知道派对的位置。

2 个答案:

答案 0 :(得分:2)

[全部:这个答案是在假设对rand()的调用是问题的一部分的情况下编写的。我在这个假设下提供了改进。 OP姗姗来迟地澄清他只用rand来告诉我们x和y值的范围(以及可能的分布)。不清楚他是否也意味着你的价值。无论如何,享受我对他没有真正构成的问题的改进答案。

我认为您最好将其重新编码为:

int u, x, y;
x = rand() % 100 - 50;
y = rand() % 100 - 50;

if ( y > x) u = 5;
else if (-y > x) u = 4;
else u = rand() % 4;

这比OP的原始代码仅调用最后一个rand的1/4。 因为我认为兰德(以及分歧)要贵得多 比起比较和分支,这将是一个显着的节省。

如果您的rand生成器在每次调用时产生大量真正随机位(例如16),您可以只调用一次(我假设rand比divide,YMMV更贵):

int u, x, y, t;
t = rand() ;
u = t % 4;
t = t >> 2;
x = t % 100 - 50;
y = ( t / 100 ) %100 - 50;

if ( y > x) u = 5;
else if (-y > x) u = 4;

我认为如果你想要真正随机的值,那么MS C库中的rand函数就不够好了。我必须自己编码;无论如何,结果更快。

你也可以通过使用乘以倒数(未经测试)来摆脱鸿沟:

int u, x, y;
unsigned int t;
unsigned long t2;
t = rand() ;
u = t % 4;

{ // Compute value of x * 2^32 in a long by multiplying.
  // The (unsigned int) term below should be folded into a single constant at compile time.
  // The remaining multiply can be done by one machine instruction
  // (typically 32bits * 32bits --> 64bits) widely found in processors.
  // The "4" has the same effect as the t = t >> 2 in the previous version
  t2 = ( t * ((unsigned int)1./(4.*100.)*(1<<32));
}
x = (t2>>32)-50; // take the upper word (if compiler won't, do this in assembler)
{ // compute y from the fractional remainder of the above multiply,
  // which is sitting in the lower 32 bits of the t2 product
  y = ( t2 mod (1<<32) ) * (unsigned int)(100.*(1<<32));
}

if ( y > x) u = 5;
else if (-y > x) u = 4;

如果你的编译器没有产生&#34;权利&#34;说明,编写汇编代码应该是直截了当的。

答案 1 :(得分:0)

使用数组索引的一些技巧,如果编译器/ CPU具有将比较结果转换为0-1值的一步指令(例如x86&#39; s&#34; sete&#34;和类似的话),它们可能会非常快)。

int ycpx[3];

/* ... */
ycpx[0] = 4;
ycpx[1] = u;
ycpx[2] = 5;
u = ycpx[1 - (-y <= x) + (y > x)];

替代表格

int v1[2];
int v2[2];

/* ... */
v1[0] = u;
v1[1] = 5;
v2[1] = 4;
v2[0] = v1[y > x];
u = v2[-y > x];

几乎不可读......

注意:在这两种情况下,包含4和5的数组元素的初始化可以包含在声明中,如果重入对您来说不是问题,则可以使数组成为静态。