0

我有一个表情

x += y;

并且,基于布尔值,我希望能够将其更改为

x -= y;

我当然可以

if(i){x+=y;} else{x-=y;}
//or
x+=(y*sign);  //where sign is either 1 or -1

但是如果我必须迭代地这样做,我想避免额外的计算。有没有更有效的方法?是否可以调制运算符?

4

6 回答 6

6
if (i) {x += y;} else {x -= y;}

可能会像您可以做的任何其他事情一样高效。 y * sign可能相当昂贵(除非编译器可以确定y保证为 1 或 -1)。

于 2011-08-27T01:58:57.120 回答
3

迭代执行此操作的最有效方法是预先计算所需的数据。

所以,预计算:

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.,

于 2011-08-27T02:16:35.783 回答
2

在现代流水线机器上,在性能确实很重要的情况下,如果可能的话,您希望避免分支。当管道的前端遇到一个分支时,CPU 会猜测要采用哪个分支,并根据该猜测让管道继续工作。如果猜对了,一切都很好。如果猜测错误,一切都不是那么好,特别是如果您仍在使用英特尔的处理器之一,例如遭受管道膨胀的奔腾 4。英特尔发现过多的流水线并不是一件好事。

更现代的处理器仍然使用流水线(Core 线的流水线长度为 14 左右),因此避免分支仍然是要做的好事之一——当它很重要时,就是这样。当你的代码不算数时,不要让你的代码变成丑陋的、过早优化的烂摊子。

最好的办法是首先找出你的表现恶魔在哪里。百分之一的代码库的一小部分是几乎所有 CPU 使用率的原因,这一点并不罕见。优化 99.9% 对 CPU 使用没有贡献的代码不会解决您的性能问题,但会对维护产生有害影响。

一旦找到罪魁祸首代码,您就可以进行优化,即使那样,也可能不会。当性能无关紧要时,不要优化。性能作为一个指标与几乎所有其他代码质量指标背道而驰。

所以,离开肥皂盒,让我们假设一小段代码是性能的罪魁祸首。尝试两种方法并进行测试。尝试您还没有想到的第三种方法并进行测试。有时,性能最佳的代码出奇地不直观。想想达夫的设备。

于 2011-08-27T02:43:00.780 回答
2

如果 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;
    }
}

顺便说一句,一个体面的编译器会为您进行优化,因此您在实际进行基准测试时可能看不到差异。

于 2011-08-27T02:54:29.980 回答
1

听起来你想避免分支和乘法。假设开关i设置为所有1位,大小与 相同y,当您想要添加时,以及0当您想要减去时。然后:

x += (y & i) - (y & ~i)

没有测试过,这只是给你一个大致的想法。请记住,这会使代码更难阅读,以换取效率的微小提高。

编辑:或者,正如 bdonlan 在评论中指出的那样,甚至可能减少。

于 2011-08-27T02:04:26.683 回答
0

我在测试的评论中提出了我的建议,在一个简单的测试中,位摆弄比 Intel(R) Xeon(R) CPU L5520 @ 2.27GHz 上的分支选项更快,但在我的笔记本电脑 Intel Core Duo 上速度较慢。

如果您可以自由地给出i0(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 和优化级别。

与往常一样,结论是基准测试是找出在您的特定情况下哪个更快的唯一方法。

于 2011-08-27T17:32:19.030 回答