確実に知る唯一の方法は、テストすることです。次の出力を効率的に生成するには、かなり賢いコンパイラが必要であることに同意する必要があります。
if(n%2) // or (n%2==0) and flip the order
n=n-1
else
n=n+1
の場合とn ^= 1;
同様ですが、最近、確実に言えるほど類似したものを確認していません。
2 番目の質問については、違いがあるとは思えません。これらの方法のいずれについても、等値比較は高速になります。速度が必要な場合、主に行うべきことは、ブランチがまったく関与しないようにすることです。たとえば、次のようなものです。
if (a == b)
c += d;
次のように記述できます c += d * (a==b);
。アセンブリ言語を見ると、2 番目の言語は少し面倒に見えることがよくあります (フラグから通常のレジスタへの比較の結果を取得するための見苦しい) が、分岐を回避することでパフォーマンスが向上することがよくあります。
編集: 少なくとも私が手元にあるコンパイラ (gcc & MSVC) は、 の を生成しませんが、cmov
の をif
生成しsete
ます* (a==b)
。コードをテスト可能なものに拡張しました。
Edit2: Potatoswatter は、乗算の代わりにビットごとの AND を使用して別の可能性をもたらしたので、他のものと一緒にそれをテストすることにしました。これを追加したコードは次のとおりです。
#include <time.h>
#include <iostream>
#include <stdlib.h>
int addif1(int a, int b, int c, int d) {
if (a==b)
c+=d;
return c;
}
int addif2(int a, int b, int c, int d) {
return c += d * (a == b);
}
int addif3(int a, int b, int c, int d) {
return c += d & -(a == b);
}
int main() {
const int iterations = 50000;
int x = rand();
unsigned tot1 = 0;
unsigned tot2 = 0;
unsigned tot3 = 0;
clock_t start1 = clock();
for (int i=0; i<iterations; i++) {
for (int j=0; j<iterations; j++)
tot1 +=addif1(i, j, i, x);
}
clock_t stop1 = clock();
clock_t start2 = clock();
for (int i=0; i<iterations; i++) {
for (int j=0; j<iterations; j++)
tot2 +=addif2(i, j, i, x);
}
clock_t stop2 = clock();
clock_t start3 = clock();
for (int i=0; i<iterations; i++) {
for (int j=0; j<iterations; j++)
tot3 +=addif3(i, j, i, x);
}
clock_t stop3 = clock();
std::cout << "Ignore: " << tot1 << "\n";
std::cout << "Ignore: " << tot2 << "\n";
std::cout << "Ignore: " << tot3 << "\n";
std::cout << "addif1: " << stop1-start1 << "\n";
std::cout << "addif2: " << stop2-start2 << "\n";
std::cout << "addif3: " << stop3-start3 << "\n";
return 0;
}
次に、非常に興味深い部分です。3 番目のバージョンの結果は非常に興味深いものです。MS VC++ の場合、ほとんどの人がおそらく期待するであろう大まかな結果が得られます。
Ignore: 2682925904
Ignore: 2682925904
Ignore: 2682925904
addif1: 4814
addif2: 3504
addif3: 3021
の&
代わりに を使用すると*
、明確な改善が得*
られif
ます。ただし、gcc を使用すると、結果はかなり異なります。
Ignore: 2680875904
Ignore: 2680875904
Ignore: 2680875904
addif1: 2901
addif2: 2886
addif3: 7675
この場合、 を使用するコードは を使用するコードの速度にはるかif
に近いです*
が、 を使用するコード&
はどちらよりも遅くなります。誰かが気にかけている場合に備えて、これは驚くべきことであり、異なるフラグで数回再コンパイルし、それぞれで数回再実行するなど、結果は完全に一貫していました.使用するコードは一貫してかなり遅くなりました. .&
gcc でコンパイルされたコードの 3 番目のバージョンでの悪い結果は、私が最初に言ったことに戻ります [そしてこの編集を終了します]:
最初に言ったように、「確実に知る唯一の方法はテストすることです」-しかし、少なくともこの限られたテストでは、乗算は一貫してif
. コンパイラー、コンパイラー・フラグ、CPU、データ・パターン、反復回数などのいくつかの組み合わせが、乗算よりも優先される場合があります。if
違いが十分に小さいため、別の方向に進むテストが完全に信頼できることは間違いありません。とはいえ、これは知っておく価値のあるテクニックだと思います。主流のコンパイラと CPU では、かなり効果的です (ただし、gcc よりも MSVC の方が確実に役立ちます) 。
[edit2 の再開:] gcc を使用した結果は、&
1) マイクロ最適化がコンパイラ固有である可能性、および 2) 実際の結果が期待とどの程度異なる可能性があるかを示しています。