私には表現があります
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 では、2 つのバージョンの が生成されます。1 つは命令myFunc
付きで、もう 1 つは命令付きです。add
sub
乾杯 & hth.,
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;
}
}
ところで、まともなコンパイラがあなたのためにその最適化を行うので、実際にベンチマークを行うときに違いが見られないかもしれません。
最新のパイプラインマシンでは、パフォーマンスが本当に重要な場合に、可能な限り分岐を避けたいと考えています。パイプラインの最前線がブランチにヒットすると、CPUはどのブランチを取るかを推測し、その推測に基づいてパイプラインを先に進めます。推測が正しければ、すべてがうまくいきます。推測が間違っていた場合、特にパイプラインの肥大化に悩まされていたPentium 4などのIntelのプロセッサのいずれかをまだ使用している場合は、すべてがうまくいくわけではありません。Intelは、パイプラインが多すぎるのは良いことではないことを発見しました。
最近のプロセッサは依然としてパイプラインを使用しているため(コアラインのパイプラインの長さは14程度)、分岐を回避することは依然として良いことの1つです。数えられないときに、コードを醜く、時期尚早に最適化された混乱にしないでください。
最善の方法は、最初にパフォーマンスデーモンがどこにあるかを見つけることです。コードベースの1%のごく一部が、ほとんどすべてのCPU使用率の原因になることはまったく珍しいことではありません。CPU使用率に寄与しないコードの99.9%を最適化しても、パフォーマンスの問題は解決されませんが、メンテナンスに悪影響を及ぼします。
犯人のコードを見つけたら最適化しますが、それでもそうではないかもしれません。パフォーマンスが重要でない場合は、最適化しないでください。メトリックとしてのパフォーマンスは、他のほとんどすべてのコード品質メトリックに反します。
それで、石鹸箱から降りて、コードの小さな断片がパフォーマンスの原因であると仮定しましょう。両方のアプローチとテストを試してください。まだ考えていない3番目のアプローチを試して、テストしてください。パフォーマンスの面で最高のコードは、驚くほど直感的でない場合があります。ダフのデバイスを考えてみてください。
分岐と乗算を避けたいようです。スイッチがと同じサイズi
のすべてのビットに設定されているとします。それで:1
y
0
x += (y & i) - (y & ~i)
テストしていませんが、これは一般的なアイデアを提供するためのものです。これにより、おそらく非常にわずかな効率の向上と引き換えに、コードが非常に読みにくくなることを覚えておいてください。
編集:または、bdonlanがコメントで指摘しているように、おそらく減少さえします。
私の提案をテストへのコメントに入れました。簡単なテストでは、ビットをいじるのはIntel(R)Xeon(R)CPU L5520 @ 2.27GHzの分岐オプションよりも高速ですが、ラップトップのIntelCoreDuoでは低速です。
(+の場合)または(-の場合)i
のいずれかの値を自由に指定できる場合、これらのステートメントは同等です。0
~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倍上回る可能性があります。
結論は、いつものように、ベンチマークは特定の状況でどちらが速いかを見つける唯一の方法であるということです。