最近我意识到我做了太多分支而不关心它对性能的负面影响,因此我决定尝试学习所有关于不分支的知识。这里有一个更极端的例子,试图使代码有尽可能少的分支。
因此对于代码if(expression)
A = C; //A and C have to be the same type here obviously
表达式可以是A == B,或Q<=B,它可以是任何可以解析为真或假的东西,或者我想把它看作是结果为1或0的
我想出了这个非分支版本
A += (expression)*(C-A); //Edited with thanks
所以我的问题是,这是一个很好的解决方案,最大限度地提高效率吗?如果是,为什么?如果不是,为什么? 取决于编译器、指令集、优化器等。当您使用布尔表达式作为int
值时,例如,(A == B) * C
,编译器必须进行比较,并根据结果将某些寄存器设置为0或1。有些指令集除了分支之外,可能没有其他方法可以做到这一点。一般来说,最好编写简单、直接的代码,让优化器来计算,或者找到分支较少的不同算法。
天哪,不要那样做!
任何"惩罚[s][你]很多分支"的人都希望你因为使用这种糟糕的东西而打包。
有多可怕,让我细数:
- 不能保证你可以乘以一个数量(例如:,
C
)由布尔值(,例如:,(A==B)
生成true
或false
)。有些语言会,有些不会。 - 任何随便读它的人都会观察到一个计算,而不是一个赋值语句。
- 你用两个比较、两个乘法、一个减法和一个加法代替了一个比较和一个条件分支。认真好不。
- 它只适用于整数数量。尝试使用各种浮点数或对象进行此操作,如果你真的很幸运,它将被编译器/解释器/任何东西拒绝。
只有当您分析了程序的运行时属性并确定这里经常存在分支错误预测,并且这会导致实际性能问题时,您才应该考虑这样做。它使代码变得不那么清晰,而且一般来说,它并不明显会更快(在您感兴趣的情况下,这也是您必须衡量的东西)。
经过研究,我得出结论,当出现瓶颈时,最好包含定时分析器,因为这类代码通常不可移植,主要用于优化。
我读了下面的问题后得到的一个确切的例子
为什么处理排序数组比处理未排序数组更快?
我用它在c++上测试了我的代码,由于额外的算术,我的实现实际上更慢。
然而!对于
下面的例子if(expression) //branched version
A += C;
//OR
A += (expression)*(C); //non-branching version
时机正是如此。分支排序列表耗时约2秒。
分支未排序列表耗时约10秒。
我的实现(无论排序还是未排序)都是3秒。
这表明,在一个未排序的瓶颈区域,当我们有一个微不足道的分支,可以简单地用一个乘法代替。
考虑我所建议的实现可能更值得。**再次强调,这主要针对被视为瓶颈的区域**