<
よりも安い(速い)<=
、そして同様に、より安い>
(速い)?>=
免責事項:私は測定できることを知っていますが、それは私のマシン上でのみであり、答えが「実装固有」またはそのようなものである可能性があるかどうかはわかりません。
<
よりも安い(速い)<=
、そして同様に、より安い>
(速い)?>=
免責事項:私は測定できることを知っていますが、それは私のマシン上でのみであり、答えが「実装固有」またはそのようなものである可能性があるかどうかはわかりません。
4つのオペレーターはすべてほぼ同時に実行されるため、4つのオペレーターの間にほとんどまたはまったく違いがないように見えます(システムによって異なる場合があります)。したがって、疑わしい場合は、状況に最も適した演算子を使用してください(特に、C ++をいじる場合)。
それで、これ以上の苦労なしに、ここに長い説明があります:
整数比較を想定:
アセンブリが生成される限り、結果はプラットフォームに依存します。私のコンピューター(Apple LLVMコンパイラ4.0、x86_64)では、結果(生成されたアセンブリは次のとおりです):
a < b (uses 'setl'):
movl $10, -8(%rbp)
movl $15, -12(%rbp)
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setl %cl
andb $1, %cl
movzbl %cl, %eax
popq %rbp
ret
a <= b (uses 'setle'):
movl $10, -8(%rbp)
movl $15, -12(%rbp)
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setle %cl
andb $1, %cl
movzbl %cl, %eax
popq %rbp
ret
a > b (uses 'setg'):
movl $10, -8(%rbp)
movl $15, -12(%rbp)
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setg %cl
andb $1, %cl
movzbl %cl, %eax
popq %rbp
ret
a >= b (uses 'setge'):
movl $10, -8(%rbp)
movl $15, -12(%rbp)
movl -8(%rbp), %eax
cmpl -12(%rbp), %eax
setge %cl
andb $1, %cl
movzbl %cl, %eax
popq %rbp
ret
それは私に多くを語っていません。したがって、ベンチマークにスキップします。
そして、ご列席の皆様、結果が出ました。私は次のテストプログラムを作成しました(「時計」はこのような結果を計算するための最良の方法ではないことを認識していますが、今はそうする必要があります)。
#include <time.h>
#include <stdio.h>
#define ITERS 100000000
int v = 0;
void testL()
{
clock_t start = clock();
v = 0;
for (int i = 0; i < ITERS; i++) {
v = i < v;
}
printf("%s: %lu\n", __FUNCTION__, clock() - start);
}
void testLE()
{
clock_t start = clock();
v = 0;
for (int i = 0; i < ITERS; i++)
{
v = i <= v;
}
printf("%s: %lu\n", __FUNCTION__, clock() - start);
}
void testG()
{
clock_t start = clock();
v = 0;
for (int i = 0; i < ITERS; i++) {
v = i > v;
}
printf("%s: %lu\n", __FUNCTION__, clock() - start);
}
void testGE()
{
clock_t start = clock();
v = 0;
for (int i = 0; i < ITERS; i++) {
v = i >= v;
}
printf("%s: %lu\n", __FUNCTION__, clock() - start);
}
int main()
{
testL();
testLE();
testG();
testGE();
}
これは、私のマシン(でコンパイルされた-O0
)で、これを私に与えます(5つの別々の実行):
testL:337848 testLE:338237 testG:337888 testGE:337787 testL:337768 testLE:338110 testG:337406 testGE:337926 testL:338958 testLE:338948 testG:337705 testGE:337829 testL:339805 testLE:339634 testG:337413 testGE:337900 testL:340490 testLE:339030 testG:337298 testGE:337593
これらの演算子の違いはせいぜいわずかであり、現代のコンピューティングの世界ではそれほど重要ではないと私は主張します。
それはさまざまです。まず、さまざまな命令セットと、コンパイラがそれらの命令セットをどのように使用するかを調べることから始めます。たとえば、openrisc 32を取り上げます。これは、明らかにmipsに触発されていますが、条件文の動作が異なります。or32の場合、フラグの比較と設定の命令があります。符号なし以下の場合はこれら2つのレジスタを比較してからフラグを設定し、等しい場合はフラグを設定します。次に、2つの条件付き分岐命令がフラグセットで分岐し、フラグクリアで分岐します。コンパイラはこれらのパスの1つに従う必要がありますが、以下、以下、以下、より大きいなどはすべて同じ数の命令を使用し、条件付き分岐には同じ実行時間を使用し、実行しない場合は同じ実行時間を使用します。条件付きブランチ。
これで、ほとんどのアーキテクチャで、パイプをフラッシュして再充填する必要があるため、分岐を実行しないよりも分岐を実行する方が時間がかかることは間違いありません。その問題を解決するために分岐予測などを行うものもあります。
一部のアーキテクチャでは、命令のサイズが異なる場合があります。gpr0とgpr1を比較し、gpr0とイミディエート番号1234を比較すると、より大きな命令が必要になる場合があります。たとえば、x86でこれがよく見られます。したがって、どちらの場合も、エンコード方法よりも少ない場合はブランチになる可能性がありますが、どのレジスタがどの値を保持するかによって、パフォーマンスの違いが生じる可能性があります(x86がこれらの問題を補うために、多くのパイプライン処理、多くのキャッシュなどを実行することを確認してください) )。別の同様の例はmipsとor32で、r0は常にゼロであり、実際には汎用レジスタではありません。書き込みを行っても変化せず、ゼロに配線されているため、0に等しい場合に比較するとコストがかかる可能性があります。比較が行われるようにgprをその即時で埋めるために追加の命令または2つが必要な場合、他の数と等しい場合は比較以上のもの、
一部のアーキテクチャには、armのような条件付き実行があり、フルアーム(サムではない)命令の場合、命令ごとに実行できるため、コードがある場合
if(i==7) j=5; else j=9;
armの擬似コードは次のようになります
cmp i,#7
moveq j,#5
movne j,#7
実際の分岐はないので、非常に高速にフライホイールでパイプラインの問題が発生することはありません。
あるアーキテクチャから別のアーキテクチャへの興味深い比較である場合は、mips、or32など、比較のために特定の種類の命令を実行する必要があります。x86、msp430などのその他のアーキテクチャでは、各alu操作でフラグ、アーム、およびフラグを変更するように指示した場合はフラグを変更するように、それ以外の場合は上記のようにしないでください。だから
while(--len)
{
//do something
}
ループ1の減算もフラグを設定します。ループ内のものが十分に単純である場合は、すべてを条件付きにすることができるため、個別の比較命令と分岐命令を節約し、パイプラインペナルティを節約できます。Mipsはこれを比較によって少し解決し、ブランチは1つの命令であり、ブランチの後に1つの命令を実行して、パイプに少し節約します。
一般的な答えは、違いは見られないということです。命令の数、実行時間などは、さまざまな条件で同じです。小さなイミディエートと大きなイミディエートなどの特殊なケースは、コーナーケースに影響を与える可能性があります。または、コンパイラーは、実行する比較に応じて、すべてを異なる方法で実行することを選択する場合があります。アルゴリズムを書き直して同じ答えが得られるようにしようとしたが、大なり記号ではなく小なり記号を使用した場合、別の命令ストリームを取得するのに十分なコードを変更している可能性があります。同様に、単純すぎるパフォーマンステストを実行した場合、コンパイラは比較を完全に最適化し、結果を生成するだけで済みます。結果は、テストコードによって異なり、異なる実行が発生する可能性があります。これらすべての鍵は、比較したいものを分解して、手順がどのように異なるかを確認することです。これにより、実行の違いが見られるかどうかがわかります。