C++,调整表达式中符号(+/-)的最有效方法

C++, most efficient way to modulate sign (+/-) in an expression

本文关键字:方法 有效 调整 表达式 符号 C++      更新时间:2023-10-16

我有一个表达式

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( "%dn", 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。英特尔发现过多的流水线操作不是一件好事。

更现代的处理器仍然使用流水线(核心线的流水线长度约为14(,因此避免分支仍然是一件好事——当它重要的时候。当它不重要的时候,不要让你的代码变得丑陋、过早优化。

最好的做法是首先找出你的表演恶魔在哪里。几乎所有CPU使用率都是由代码库的1%的一小部分引起的,这并不罕见。优化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在评论中指出的那样,甚至可能减少。

我在评论中提出了我的建议进行测试,在一个简单的测试中,篡改比英特尔(R(至强(R(CPU L5520 2.27GHz上的分支选项更快,但在我的笔记本电脑英特尔酷睿双核上更慢。

如果您可以自由地给i0(表示+(或~0(表示-(,则这些语句等效:

// 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);

如上所述,根据CPU和使用的优化级别,一种方法可以以2倍的优势击败另一种方法。

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