我有一个简单的问题,假设我有以下代码,例如,它以类似的方式重复10次。
if blah then
number = number + 2^n
end if
评估会更快:
number = number + blah*2^n?
这也带来了问题,你可以将布尔值乘以整数(虽然我不确定从2 ^ n返回的类型,是整数还是unsigned..etc)? (我在Ada工作,但是我们可以试着概括一下吗?)
编辑:对不起我应该澄清我正在考虑n的力量,我把c放在那里因为我对将来学习感兴趣如果我在c中碰到这个问题并且我认为那里在这些主板上有更多的c程序员然后Ada(我假设你知道这意味着什么),但是我目前的问题是Ada语言,但问题应该是相当语言独立的(我希望)。
答案 0 :(得分:10)
这个问题没有一般的答案,这在很大程度上取决于你的编译器和CPU。现代CPU有条件移动指令,所以一切皆有可能。
这里要知道的唯一方法是检查生成的汇编程序(通常-S
作为编译器选项)并进行测量。
答案 1 :(得分:5)
在阿达......
原始配方:
if Blah then
Number := Number + (2 ** N);
end if;
替代通用公式,假设Blah的类型为布尔值,而数字和N的类型合适:
Number := Number + (Boolean'pos(Blah) * (2 ** N));
(对于用户定义的整数或浮点类型的N
和Number
,可能需要合适的定义和类型转换,这里的关键点是Boolean'pos()
构造,Ada保证将为预定义的布尔类型提供0或1。)
至于这是否更快,我同意@Cthutu:
我会保留条件。 你不应该担心低级别 此时优化细节。 写下描述你的代码 算法最好,相信你的 编译器。
答案 2 :(得分:5)
如果我们谈论C而且blah不在你的控制之内,那么就这样做:
if(blah) number += (1<<n);
C中确实没有布尔值,并且不需要,false为零且true不为零,因此您不能假设零不是1,这是您的解决方案所需要的,也不能假设blah中的任何特定位都被设置,例如:
number += (blah&1)<<n;
不一定有效,因为0x2或0x4或任何非零,清零位被认为是真的。通常你会发现0xFFF ... FFFF(减1或全部)用作true,但你不能依赖典型的。
现在,如果您完全控制了blah中的值,并将其严格保持为0表示false而1表示true,那么您可以执行您所询问的内容:
number += blah<<n;
避免分支,额外缓存行填充等的可能性
回到通用案例,采用这种通用解决方案:
unsigned int fun ( int blah, unsigned int n, unsigned int number ) { if(blah) number += (1<<n); return(number); }
编译两个最受欢迎/最常用的平台:
testl %edi, %edi movl %edx, %eax je .L2 movl $1, %edx movl %esi, %ecx sall %cl, %edx addl %edx, %eax .L2:
以上使用条件分支。
下面的一个使用条件执行,没有分支,没有管道刷新,是确定性的。
cmp r0,#0 movne r3,#1 addne r2,r2,r3,asl r1 mov r0,r2 bx lr
可以通过在函数调用中重新排列参数来保存mov r0,r2指令,但这是学术性的,你通常不会在这个上烧掉函数调用。
编辑:
正如所建议:
unsigned int fun ( int blah, unsigned int n, unsigned int number ) { number += ((blah!=0)&1)<<n; return(number); }
subs r0, r0, #0 movne r0, #1 add r0, r2, r0, asl r1 bx lr
当然更便宜,而且代码看起来不错,但我不会假设blah!= 0的结果,即0或编译器定义为true的任何内容总是设置为lsbit。它不必为编译器设置该位以生成工作代码。也许标准规定了真实的具体价值。通过重新排列函数参数,if(blah)数字+ = ...也将导致三个单时钟指令而没有假设。
EDIT2:
看看我理解为C99标准:
==(等于)和!=(不等于 运营商类似于 关系运算符除了他们的 优先级较低。每一个 运算符如果指定则产生1 relation为true,如果为false,则为0。
这解释了为什么上面的编辑工作以及为什么你得到了movne r0,#1而不是其他随机数。
海报在询问关于C的问题,但也注意到ADA是当前的语言,从语言独立的角度来看,你不应该假设像上面的C特征这样的“特征”并使用if(blah)数字=数字+(1 海报假设基本上也是正确的,如果你可以把它变成0或1形式,那么在数学中使用它会更快,因为没有分支。如果没有它比分支更昂贵,那么将它变成那种形式就是诀窍。
答案 3 :(得分:4)
我会保留条件。此时您不应该担心低级优化细节。编写最能描述算法的代码并信任编译器。在某些CPU上,乘法较慢(例如,在每条指令上都有条件的ARM处理器)。你也可以使用?:表达式,它可以在某些编译器下更好地优化。例如:
number += (blah ? 2^n : 0);
如果出于某种原因,这个小计算是分析后应用程序的瓶颈,那么就会担心低级优化。
答案 4 :(得分:4)
在C中,关于blah * 2 ^ n:你有理由相信blah取值0和1吗?该语言仅承诺0&lt; - &gt; FALSE和(其他所有)&lt; - &gt;真正。 C允许您将“布尔”临时值与另一个数字相乘,但结果未定义,除非结果= 0&lt; =&gt; bool是假的,或者数字是零。
在Ada中,关于blah * 2 ^ n:语言没有在Boolean类型上定义乘法运算符。因此,blah不能成为一个布尔并且会成倍增加。
答案 5 :(得分:1)
如果你的语言允许在布尔值和数字之间进行乘法,那么是的,这比条件更快。条件需要分支,这可能使CPU的管道无效。此外,如果分支足够大,它甚至可能导致指令中的高速缓存未命中,尽管在您的小例子中这不太可能。
答案 6 :(得分:1)
通常,特别是在与Ada合作时,您不应该担心像这样的微优化问题。编写代码,以便读者清楚,只有在遇到性能问题时才会担心性能,并将其跟踪到代码的那一部分。
不同的CPU有不同的需求,它们可能非常复杂。例如,在这种情况下,更快的取决于CPU的管道设置,当时缓存中的内容以及分支预测单元的工作方式。编译器的一部分工作就是成为这方面的专家,除了最好的汇编程序员之外,它会做得更好。 Certianly比你(或我)更好。
所以你只需要担心编写好的代码,让编译器担心从中获取高效的机器代码。
答案 7 :(得分:1)
对于所述的问题,C中确实存在可以产生有效代码的简单表达式。
n
运算符的2
幂可以使用<<
运算符计算为1 << n
,前提是n
小于int
中的值位数blah
。
如果int
是布尔,即0
,其值为1
或number += blah << n;
,则可以编写代码片段:
blah
如果if (blah)
是可以测试其真值为number += !!blah << n;
的任何标量类型,则表达式稍微复杂一些:
number += (blah != 0) << n;
相当于l1 = [(['hello','world'],), (['stack','overflow'],), (['hello', 'alice'],), (['sample', 'text'],)]
df1 = spark.createDataFrame(l1)
l2 = [(['big','world'],), (['sample','overflow', 'alice', 'text', 'bob'],), (['hello', 'sample'],)]
df2 = spark.createDataFrame(l2)
测试仍然存在但是,对于现代体系结构,生成的代码不会有任何跳转,因为可以使用Godbolt's compiler explorer在线验证。
答案 8 :(得分:0)
在任何一种情况下,你都无法避免分支(内部),所以不要试试!
在
number = number + blah*2^n
除非编译器足够聪明,否则当blah为0时,必须对整个表达式进行求值。如果是,如果blah为0,你将得到一个分支。如果不是,你总是得到一个昂贵的乘。如果blah为false,您还将获得不必要的添加和赋值。
在“if then”语句中,语句只会在blah为真时执行add和赋值。
简而言之,在这种情况下,您的问题的答案是“是”。
答案 9 :(得分:0)
此代码显示它们的表现相似,但乘法通常稍快。
@Test
public void manual_time_trial()
{
Date beforeIfElse = new Date();
if_else_test();
Date afterIfElse = new Date();
long ifElseDifference = afterIfElse.getTime() - beforeIfElse.getTime();
System.out.println("If-Else Diff: " + ifElseDifference);
Date beforeMultiplication = new Date();
multiplication_test();
Date afterMultiplication = new Date();
long multiplicationDifference = afterMultiplication.getTime() - beforeMultiplication.getTime();
System.out.println("Mult Diff : " + multiplicationDifference);
}
private static long loopFor = 100000000000L;
private static short x = 200;
private static short y = 195;
private static int z;
private static void if_else_test()
{
short diff = (short) (y - x);
for(long i = 0; i < loopFor; i++)
{
if (diff < 0)
{
z = -diff;
}
else
{
z = diff;
}
}
}
private static void multiplication_test()
{
for(long i = 0; i < loopFor; i++)
{
short diff = (short) (y - x);
z = diff * diff;
}
}