1

私が C とアセンブリを学んでいた頃、速度を上げるには単純な比較を使用する方がよいと教えられました。たとえば、次のように言うとします。

if(x <= 0)

if(x < 1)

どちらがより速く実行されますか? 私の議論 (これは間違っているかもしれません) は、比較が 1 つしかないため、ほとんどの場合、2 番目の方が高速に実行されるというものです)。

数値が 0 未満の場合、これは true と同等であるため、最初の実行は高速に実行されますが、等しいかどうかをチェックして 2 番目と同じくらい高速にする必要はありませんが、数値が 0 以上の場合は常に遅くなります。次に、それが 0 に等しいかどうかを確認するために 2 回目の比較を行う必要があります。

私は現在 C# を使用しており、デスクトップ向けの開発速度は問題ではありませんが (少なくとも彼の指摘が議論に値するほどではありません)、モバイル デバイス向けの開発も行っているため、そのような議論を考慮する必要があると思います。デスクトップほど強力ではなく、そのようなデバイスでは速度が問題になります。

さらに検討するために、整数 (10 進数なし) と、-1 や -12,345などの負の数が存在できない数 (エラーがない限り) について話しています。負の数のアイテムがあるが、リストが空かどうかを確認したい (または、問題がある場合は、エラーを示すために x の値を負に設定します。たとえば、リストにいくつかのアイテムがあるが、何らかの理由でリスト全体を取得し、これを示すために数値を負に設定します。これは、アイテムがないと言うことと同じではありません)。

上記の理由で、私は意図的に明らかな部分を省略しました

if(x == 0)

if(x.isnullorempty())

およびアイテムのないリストを検出するためのその他のアイテム。

繰り返しになりますが、検討のために、おそらく前述の機能を持つ SQL ストアド プロシージャを使用して、データベースからアイテムを取得する可能性について話しています (つまり、標準 (少なくともこの会社では) は、問題を示すために負の数を返すことです)。

このような場合、上記の 1 番目と 2 番目の項目のどちらを使用するのがよいでしょうか。

4

9 に答える 9

19

それらは同一です。どちらも他より速いわけではありません。xが整数であると仮定すると、どちらもまったく同じ質問をします。C# はアセンブリではありません。求めている効果を得るために最適なコードを生成するようコンパイラーに要求しています。その結果を取得する方法を指定していません。

この回答も参照してください。

私の議論 (これは間違っているかもしれません) は、比較が 1 つしかないため、ほとんどの場合、2 番目の方が高速に実行されるというものです)。

明らかにそれは間違っています。それが正しいと仮定するとどうなるか見てみましょう:

<<=質問が少ないため、より高速です。(あなたの主張。)

><=は、同じ質問をするため、回答が逆になるだけで、同じ速度です。

したがって、 !<よりも高速です。>しかし、この同じ引数は>、よりも高速であることを示しています<

「逆の答えだけ」は、追加のブール演算に忍び込んでいるように見えるので、この答えに従うかどうかわかりません。

同じ理由で、それは間違っています (シリコンの場合、ソフトウェアの場合は正しい場合もあります)。検討:

3 != 4は、逆の答え、追加のブール演算を使用する3 == 4ため、 よりも計算に時間がかかります。3 != 4

3 == 4は、逆の答え、追加のブール演算を使用する3 != 4ため、よりも高価です。3 != 4

したがって、3 != 4それ自体よりも高価です。

逆の答えは正反対の質問であり、追加のブール演算ではありません。または、もう少し正確に言うと、比較結果から最終的な回答へのマッピングが異なります。との両方3 == 4で、3 != 43 と 4 を比較する必要があります。その比較は、エーテルが「等しい」または「等しくない」という結果になります。質問は、「等しい」と「等しくない」を「真」と「偽」に異なる方法でマッピングするだけです。どちらのマッピングも、他のマッピングよりも高価です。

于 2013-01-17T17:12:20.673 に答える
6

少なくともほとんどの場合、いいえ、どちらにも利点はありません。

A<=は通常、2 つの個別の比較として実装されません。典型的な (x86 などの) CPU では、2 つの別個のフラグがあり、1 つは等しいことを示し、もう 1 つは負 (「未満」を意味することもあります) を示します。それに加えて、これらのフラグの組み合わせに依存する分岐があるため、 or<に変換されます(以下の場合はジャンプ、以下の場合はジャンプ -- 前者は符号付き数値用、後者は符号なし用)。Aはorに変換されます(以下の場合はジャンプ、以下の場合はジャンプ)。jljb<=jlejbe

CPU が異なれば、命令に異なる名前/ニーモニックが使用されますが、ほとんどの場合、同等の命令があります。私が認識しているすべてのケースで、それらはすべて同じ速度で実行されます。

編集: おっと -- 上で述べた一般的なルールの 1 つの例外について言及するつもりでした。厳密には<vs.から<=ではありませんが、他の数値の代わりに比較0できる場合は、少し (ごくわずかな) アドバンテージを得ることができます。たとえば、最小値に達するまでカウントダウンする変数があるとします。このような場合、1 までカウントダウンする代わりに 0 までカウントダウンできると、少し有利になる可能性があります。理由は単純です。前述のフラグは、ほとんどの命令の影響を受けます。次のようなものがあるとしましょう。

   do {
       // whatever
   } while (--i >= 1);

コンパイラはこれを次のように変換します。

loop_top:
    ; whatever

    dec i
    cmp i, 1
    jge loop_top

while (--i > 0)代わりに、0 (または)と比較すると、代わりにwhile (--i != 0)次のように変換される可能性があります。

loop_top:
    ; whatever

    dec i
    jg loop_top
    ; or: jnz loop_top

ここで、decはデクリメントの結果がゼロであったかどうかを示すためにゼロ フラグを設定/クリアします。そのため、条件は からの結果に直接基づいて、他のコードで使用されているdecを排除できます。cmp

ただし、これはたとえば 30 年以上前には非常に効果的でしたが、最新のコンパイラのほとんどは、ユーザーの助けなしでこのような変換を処理できます (ただし、一部のコンパイラは、特に小規模な組み込みシステムなどの場合、そうでない場合があります)。IOW、一般的に最適化に関心がある場合、いつか気にする可能性はほとんどありませんが、少なくとも私にとっては、C# への適用はせいぜい疑わしいようです。

于 2013-01-17T17:16:19.303 に答える
5

最近のほとんどのハードウェアには、以下の条件をチェックする命令とまったく同じ速度で実行される単一の命令で、以下の条件をチェックするための命令組み込まれています。(かなり) 古いハードウェアに適用されていた議論はもはや適用されません。最も読みやすいと思われる代替案、つまりコードの読者にアイデアをよりよく伝える代替案を選択してください。

于 2013-01-17T17:13:29.897 に答える
4

ここに私の機能があります:

public static void TestOne()
{
    Boolean result;
    Int32 i = 2;

    for (Int32 j = 0; j < 1000000000; ++j)
        result = (i < 1);
}

public static void TestTwo()
{
    Boolean result;
    Int32 i = 2;

    for (Int32 j = 0; j < 1000000000; ++j)
        result = (i <= 0);
}

同一の IL コードを次に示します。

L_0000: ldc.i4.2 
L_0001: stloc.0 
L_0002: ldc.i4.0 
L_0003: stloc.1 
L_0004: br.s L_000a
L_0006: ldloc.1 
L_0007: ldc.i4.1 
L_0008: add 
L_0009: stloc.1 
L_000a: ldloc.1 
L_000b: ldc.i4 1000000000
L_0010: blt.s L_0006
L_0012: ret 

数回のテストセッションの後、明らかに、どちらも高速ではないという結果が得られました。違いは数ミリ秒だけであり、実際の違いとは見なされず、生成される IL 出力はとにかく同じです。

于 2013-01-17T17:27:49.047 に答える
3

ARM プロセッサと x86 プロセッサの両方に、「未満」と「以下」の両方の専用命令があります (これは「より大きくない」と評価されることもあります)。最新のコンパイラ。

于 2013-01-17T17:14:22.350 に答える
1

リファクタリング中に、ロジックについて考えを変えると、否定するのif(x<=0)速くなります(つまり、エラーが発生しにくくなります)(つまりif(!(x<=0))、正しく否定されない場合と比較しif(!(x<1))て)、それはおそらくあなたが参照しているパフォーマンスではありません。;-)

于 2013-01-17T17:21:50.977 に答える
0

x<=0 が x<1 とは異なる命令にコンパイルされたとしても、パフォーマンスの違いは非常に小さいため、ほとんどの場合気にする必要はありません。コードを最適化するためのより生産的な領域が他にある可能性が非常に高いです。黄金律は、コードをプロファイリングし、現実世界で実際に遅いビットを最適化することです。仮想的に遅いと思われるビットや、理論的に可能なほど高速ではないビットではありません。また、コードを他の人が読めるようにすることにも集中してください。コンパイラの煙で消えてしまう幻のマイクロ最適化ではありません。

于 2013-01-17T17:27:53.530 に答える
0

IFx<1の方が高速である場合、最新のコンパイラは(が整数であると仮定して) に変更x<=0されます。したがって、最新のコンパイラでは、これは問題ではなく、同一のマシン コードを生成する必要があります。x<1x

于 2013-01-17T17:13:52.770 に答える
0

@Francis Rodgers、あなたは言った:

数値が 0 未満の場合、これは true と同等であるため、最初の実行は高速に実行されますが、等しいかどうかをチェックして 2 番目と同じくらい高速にする必要はありませんが、数値が 0 以上の場合は常に遅くなります。次に、それが 0 に等しいかどうかを確認するために 2 回目の比較を行う必要があります。

そして(コメントで)、

> が <= と同じであるところを説明できますか? これは私の論理世界では意味をなさないからです。たとえば、<=0 は >0 と同じではなく、まったく逆です。あなたの答えをよりよく理解できるように、例が欲しいです

あなたは助けを求め、助けが必要です。私は本当にあなたを助けたいと思っています。残念ながら、他の多くの人々もこの助けを必要としています。

より基本的なことから始めます。> のテストは <= のテストと同じではないという考えは論理的に間違っています (プログラミング言語だけでなく)。これらの図を見て、リラックスして考えてみてください。A と B で X <= Y がわかっている場合はどうなりますか? 各図で X > Y であることがわかっている場合はどうなりますか? ここに画像の説明を入力 そうです、何も変わっていません。それらは同等です。図の重要な詳細はtruefalseA と B は反対側にあります。その意味は、コンパイラ (または一般的にはデコーダ) が、両方の質問が同等になるようにプログラム フローを自由に再編成できるということです。つまり、<= を 2 つのステップに分割する必要はなく、再編成するだけです。あなたの流れに少し。非常に悪いコンパイラまたはインタプリタだけがそれを行うことができません。アセンブラとはまだ関係ありません。すべての比較に十分なフラグがない CPU の場合でも、コンパイラはその特性に最適なテストを使用して (疑似) アセンブラ コードを生成できるという考え方です。しかし、電子レベルで複数のフラグを並行してチェックする CPU の機能を追加すると、コンパイラの仕事は非常に簡単になります。http://download.intel.com/products/processor/manual/325462.pdf

とにかく、関連する状況についてもっと議論したいと思います。

0 または 1 との比較: @Jerry Coffin は、アセンブラー レベルで非常に優れた説明をしています。マシン コード レベルで深く掘り下げると、1 と比較する亜種は 1 を CPU 命令に「ハード コード」してそれを CPU にロードする必要がありますが、他の亜種はそれを行うことができませんでした。とにかく、ここでのゲインは絶対に小さいです。実際のライブ状況で速度を測定できるとは思いません。副次的なコメントとして、命令cmp i, 1は (結果を保存せずに) 一種の減算をi-1行いますが、フラグを設定すると、実際には 0 と比較されます !!

もっと重要なのは、次のような状況です: compareX<=Yまたはwith は明らかに論理的に同等ですが、 ifおよびare 式を評価する必要があり、他の結果に影響を与える可能性がある場合、Y>=X深刻な副作用が生じる可能性があります! まだ非常に悪く、未定義の可能性があります。XY

さて、図に戻って、@Jerry Coffin のアセンブラーの例も見てみましょう。ここに次の問題があります。実際のソフトウェアは、メモリ内の一種の線形チェーンです。条件の 1 つを選択し、別のプログラム メモリ位置にジャンプして続行し、反対の条件を続行します。継続する条件として、より頻繁な条件を選択することは理にかなっています。これらの状況でコンパイラにヒントを与える方法がわかりません。明らかに、コンパイラはそれ自体を理解できません。私が間違っている場合は訂正してください。しかし、この種の最適化の問題はかなり一般的であり、プログラマーはコンパイラーの助けを借りずに自分で決定する必要があります。

しかし、繰り返しになりますが、どのような状況でも、これらの局所的な小さな最適化ではなく、一般的な静止と可読性を考慮してコードを記述します。

于 2013-01-18T11:05:29.650 に答える