kyb*_*kyb 46 c c++ performance arm thumb
考虑下一个代码:
unsigned idx;
//.. some work with idx
if( idx >= idx_max )
idx %= idx_max;
Run Code Online (Sandbox Code Playgroud)
可以简化为仅第二行:
idx %= idx_max;
Run Code Online (Sandbox Code Playgroud)
并将取得同样的结果.
好几次我遇到了下一个代码:
unsigned x;
//... some work with x
if( x!=0 )
x=0;
Run Code Online (Sandbox Code Playgroud)
可以简化为
x=0;
Run Code Online (Sandbox Code Playgroud)
问题:
if,为什么?特别是使用ARM Thumb指令集. if可以省略吗?Nir*_*man 66
如果你想了解编译器正在做什么,你需要提取一些程序集.我推荐这个网站(我已经从问题中输入了代码)):https://godbolt.org/g/FwZZOb.
第一个例子更有趣.
int div(unsigned int num, unsigned int num2) {
if( num >= num2 ) return num % num2;
return num;
}
int div2(unsigned int num, unsigned int num2) {
return num % num2;
}
Run Code Online (Sandbox Code Playgroud)
产生:
div(unsigned int, unsigned int): # @div(unsigned int, unsigned int)
mov eax, edi
cmp eax, esi
jb .LBB0_2
xor edx, edx
div esi
mov eax, edx
.LBB0_2:
ret
div2(unsigned int, unsigned int): # @div2(unsigned int, unsigned int)
xor edx, edx
mov eax, edi
div esi
mov eax, edx
ret
Run Code Online (Sandbox Code Playgroud)
基本上,出于非常具体和逻辑的原因,编译器不会优化分支.如果整数除法与比较的成本大致相同,那么分支将是毫无意义的.但整数除法(模数与典型值一起执行)实际上非常昂贵:http://www.agner.org/optimize/instruction_tables.pdf.这些数字因架构和整数大小而异,但通常可能是从15到接近100个周期的延迟.
通过在执行模数之前选择分支,您实际上可以节省大量的工作.请注意:编译器也不会将没有分支的代码转换为程序集级别的分支.那是因为分支也有一个缺点:如果最终需要模数,那你就浪费了一点时间.
如果不知道idx < idx_max真实的相对频率,就无法对正确的优化做出合理的决定.所以编译器(gcc和clang做同样的事情)选择以相对透明的方式映射代码,将这个选择留给开发人员.
所以这个分支可能是一个非常合理的选择.
第二个分支应该完全没有意义,因为比较和分配具有可比的成本.也就是说,您可以在链接中看到,如果编译器具有对变量的引用,则仍然不会执行此优化.如果值是局部变量(如在演示的代码中那样),则编译器将优化分支.
总之,第一段代码可能是一个合理的优化,第二段,可能只是一个累了的程序员.
在许多情况下,使用已经存在的值写入变量可能比读取它更慢,找出已经保持所需的值,并跳过写入.某些系统具有处理器缓存,可立即将所有写入请求发送到内存.虽然这种设计在今天并不常见,但它们过去常常很常见,因为它们可以提供完整读/写缓存所能提供的大部分性能提升,但成本只是其中的一小部分.
像上面这样的代码也可以在某些多CPU情况下相关.最常见的情况是在两个或多个CPU内核上同时运行的代码将重复命中变量.在具有强大内存模型的多核缓存系统中,想要编写变量的核心必须首先与其他核心协商以获取包含它的缓存行的独占所有权,然后必须再次协商以在下次放弃此类控制时任何其他核心都想读或写它.这样的操作往往非常昂贵,即使每次写入只是存储已经存储的值,也必须承担成本.但是,如果位置变为零并且永远不会再次写入,则两个内核可以同时保留高速缓存行以进行非独占只读访问,并且永远不必进一步协商它.
在几乎所有多个CPU都可以命中变量的情况下,变量应该至少被声明volatile.可能适用的一个例外是,在开始之后发生的对变量的所有写入main()将存储相同的值,并且无论一个CPU的任何存储在另一个CPU中是否可见,代码都将正常运行.如果多次执行某些操作会浪费但是无害,并且变量的目的是说是否需要完成,然后许多实现可能能够生成更好的代码而没有volatile限定符而不是使用,只要它们不是尝试通过使写入无条件来提高效率.
顺便提一下,如果通过指针访问对象,则上述代码可能有另一个可能的原因:如果函数被设计为接受const某个字段为零的const对象,或者应该将该字段设置为的非对象零,上述代码可能是确保两种情况下定义的行为所必需的.