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

C 连续if语句的无分支转换,c,if-statement,optimization,assembly,C,If Statement,Optimization,Assembly,我一直在想如何将下面代码的最后两个“if”语句转换成无分支状态 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; 我想让我抓狂的是,那些人没有一个elsecatcher。如

我一直在想如何将下面代码的最后两个“if”语句转换成无分支状态

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
catcher。如果是这样的话,我可能已经适应了无分支的
abs
(或
max
/
min
)功能的变化

您看到的
rand()
函数不是真正代码的一部分。我这样添加它们只是为了提示变量
x
y
u
在两个分支发生时可能具有的预期范围

为此,允许使用装配机器代码

编辑:

经过一番深思熟虑后,我终于拼凑出了一个无分支的工作版本:

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%


编译器知道参与方在哪里。

[All:这个答案是在假设对rand()的调用是问题的一部分的情况下编写的。在这个假设下,我在下面提供了改进。 OP后来澄清说,他只是用rand来告诉我们x和y的值的范围(大概是分布)。不清楚他是否也指u的值。无论如何,请欣赏我对他没有真正提出的问题的改进答案]

我想你最好把这个重新编码为:

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;
这调用最后一个rand的频率仅为OP原始代码的1/4。 因为我认为兰特(和分水岭)要贵得多 与比较和分支相比,这将是一个显著的节约

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

我认为,如果您想要真正的随机值,那么msc库中的rand函数不足以实现这一点。我必须自己编写代码;结果还是快了

您还可以通过使用倒数乘法(未经测试)来消除除法:

intu,x,y;
无符号整数t;
无符号长t2;
t=rand();
u=t%4;
{//通过相乘计算长时间内x*2^32的值。
//下面的(unsigned int)项应在编译时折叠为单个常量。
//剩余的乘法可由一条机器指令完成
//(通常为32位*32位-->64位)广泛存在于处理器中。
//“4”与上一版本中的t=t>>2具有相同的效果
t2=(t*((unsigned int)1.//(4.*100.)*(132)-50;//取高位字(如果编译器不愿意,则在汇编程序中执行此操作)
{//从上述乘法的小数余数计算y,
//它位于t2乘积的低32位

y=(t2 mod(1一些使用数组索引的技巧,如果编译器/CPU有一步指令将比较结果转换为0-1值(例如x86的“sete”和类似值),它们可能会非常快

几乎无法阅读


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

u+=(5-u)*(y>x);
?@pmg比较“>”和“@pmg mmh但是为了得到这些0和1,机器必须在某个地方评估所说的条件……我试图避免。FWIW,gcc已经使用cmov编译了这个。所以甚至有必要重写它吗?为什么这很重要?你已经在这段代码中调用了“rand”3x,并进行了2次除法(除以100);我希望进一步的优化不会给您带来什么总体优势。虽然我完全同意rand()和modulo函数并不是性能关键型代码的最佳函数,但实际上我只是把它们放在那里,只是为了给读者一个提示,说明“x”、“y”和“u”的预期值范围变量可能在代码的主题部分(最重要的是“u”)时保持不变。很抱歉给您带来不便,我保证下次会更清楚。由于分支条件是随机的,它实际上将非常昂贵,因为CPU分支预测器无法提供帮助。我认为至少与除法或对rand()的调用一样昂贵(这是推测,但不是备份证据…)与所有事物一样,度量、度量、度量…然后考虑当实现技术改变时会发生什么。(如果分支仍然相对昂贵,在我的所有变体中,它们可以按照其他答案的建议转化为条件移动。@user2464424:Ah。你只是想告诉我们x和y的范围。哦,好吧。是的,下次你应该更清楚。相反,你可以编写一个断言语句:assert(x>-50&&x<50)。[我的误解让这个谜题变得有点有趣]。@user2464424:…你应该修正你的问题;这不是“下次”,人们还在继续阅读它:-{
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;
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;
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];