我有一个表情
x += y;
并且,基于布尔值,我希望能够将其更改为
x -= y;
我当然可以
if(i){x+=y;} else{x-=y;}
//or
x+=(y*sign); //where sign is either 1 or -1
但是如果我必须迭代地这样做,我想避免额外的计算。有没有更有效的方法?是否可以调制运算符?
我有一个表情
x += y;
并且,基于布尔值,我希望能够将其更改为
x -= y;
我当然可以
if(i){x+=y;} else{x-=y;}
//or
x+=(y*sign); //where sign is either 1 or -1
但是如果我必须迭代地这样做,我想避免额外的计算。有没有更有效的方法?是否可以调制运算符?
if (i) {x += y;} else {x -= y;}
可能会像您可以做的任何其他事情一样高效。 y * sign
可能相当昂贵(除非编译器可以确定y
保证为 1 或 -1)。
迭代执行此操作的最有效方法是预先计算所需的数据。
所以,预计算:
const YourNumberType increment = (i? y : -y);
然后在你的循环中:
x += increment;
#include <stdio.h>
void display( int x ) { printf( "%d\n", x ); }
template< bool isSomething >
inline void advance( int& x, int y );
template<> inline void advance<true>( int& x, int y ) { x += y; }
template<> inline void advance<false>( int& x, int y ) { x -= y; }
template< bool isSomething >
void myFunc()
{
int x = 314;
int y = 271;
for( ;; )
{
advance< isSomething >( x, y ); // The nano-optimization.
display( x );
if( !( -10000 < x && x < 10000 ) ) { return; }
}
}
int main( int n, char*[] )
{
n > 1? myFunc<true>() : myFunc<false>();
}
例如,使用 Visual C++ 10.0 这会生成两个版本的myFunc
,一个带有add
指令,另一个带有sub
指令。
干杯&hth.,
在现代流水线机器上,在性能确实很重要的情况下,如果可能的话,您希望避免分支。当管道的前端遇到一个分支时,CPU 会猜测要采用哪个分支,并根据该猜测让管道继续工作。如果猜对了,一切都很好。如果猜测错误,一切都不是那么好,特别是如果您仍在使用英特尔的处理器之一,例如遭受管道膨胀的奔腾 4。英特尔发现过多的流水线并不是一件好事。
更现代的处理器仍然使用流水线(Core 线的流水线长度为 14 左右),因此避免分支仍然是要做的好事之一——当它很重要时,就是这样。当你的代码不算数时,不要让你的代码变成丑陋的、过早优化的烂摊子。
最好的办法是首先找出你的表现恶魔在哪里。百分之一的代码库的一小部分是几乎所有 CPU 使用率的原因,这一点并不罕见。优化 99.9% 对 CPU 使用没有贡献的代码不会解决您的性能问题,但会对维护产生有害影响。
一旦找到罪魁祸首代码,您就可以进行优化,即使那样,也可能不会。当性能无关紧要时,不要优化。性能作为一个指标与几乎所有其他代码质量指标背道而驰。
所以,离开肥皂盒,让我们假设一小段代码是性能的罪魁祸首。尝试两种方法并进行测试。尝试您还没有想到的第三种方法并进行测试。有时,性能最佳的代码出奇地不直观。想想达夫的设备。
如果 i
在循环执行期间保持不变,而y
不是,则移动if
循环外部。
所以,而不是...
your_loop {
y = ...;
if (i)
x += y;
else
x -= y;
}
...请执行下列操作....
if (i) {
your_loop {
y = ...;
x += y;
}
}
else {
your_loop {
y = ...;
x -= y;
}
}
顺便说一句,一个体面的编译器会为您进行优化,因此您在实际进行基准测试时可能看不到差异。
听起来你想避免分支和乘法。假设开关i
设置为所有1
位,大小与 相同y
,当您想要添加时,以及0
当您想要减去时。然后:
x += (y & i) - (y & ~i)
没有测试过,这只是给你一个大致的想法。请记住,这会使代码更难阅读,以换取效率的微小提高。
编辑:或者,正如 bdonlan 在评论中指出的那样,甚至可能减少。
我在测试的评论中提出了我的建议,在一个简单的测试中,位摆弄比 Intel(R) Xeon(R) CPU L5520 @ 2.27GHz 上的分支选项更快,但在我的笔记本电脑 Intel Core Duo 上速度较慢。
如果您可以自由地给出i
值0
(for +) 或~0
(for -),则这些语句是等价的:
// branching:
if ( i ) sum -= add; else sum += add;
sum += i?-add:add;
sum += (i?-1:1)*add;
// bit fiddling:
sum += (add^i)+(i&1);
sum += (add^i)+(!!i);
sum += (i&~add)-(i&add);
如前所述,一种方法可以将另一种方法击败 2 倍,具体取决于所使用的 CPU 和优化级别。
与往常一样,结论是基准测试是找出在您的特定情况下哪个更快的唯一方法。