CSSのz-index
のカウントとして約10億を使用し、比較しなければならないことを考えていました。非常に大きい数値と非常に小さい数値を比較すると、ALUレベルのパフォーマンスに違いはありますか?
たとえば、これらの2つのスニペットのいずれかが他よりも高価になるでしょうか?
snippet 1
for (int i = 0; i < 10000000; i++){
if (i < 10000000000000) {
//do nothing
}
}
snippet 2
for (int i = 0; i < 10000000; i++){
if (i < 1000) {
//do nothing
}
}
私が取り組んだすべてのプロセッサは、一方のオペランドを他方から減算して結果を破棄し、プロセッサのフラグ(ゼロ、負など)のみを残して比較を行います。減算は単一の演算として行われるため、オペランドの内容は関係ありません。
質問に確実に答える最善の方法は、コードをアセンブリにコンパイルし、生成された命令についてターゲットプロセッサのドキュメントを参照することです。現在のIntel CPUの場合、これは Intel 64およびIA-32アーキテクチャソフトウェア開発者向けマニュアル になります。
CMP
( "compare")命令の説明は、PDFのボリューム2A、ページ3-126、またはページ618にあり、その操作を次のように説明しています。
temp ← SRC1 − SignExtend(SRC2);
ModifyStatusFlags; (* Modify status flags in the same manner as the SUB instruction*)
つまり、必要に応じて2番目のオペランドが符号拡張され、最初のオペランドから減算され、その結果がプロセッサの一時領域に配置されます。次に、ステータスフラグは、SUB
( "減算")命令(PDFの1492ページ)の場合と同じように設定されます。
CMP
またはSUB
のドキュメントには、オペランドの値がレイテンシに影響を与えることは記載されていないため、使用する値は安全です。
非常に大きい数値と非常に小さい数値を比較すると、ALUレベルのパフォーマンスに違いはありますか?
小さい数から大きい数に数値型を変更しない限り、たとえばint
からlong
に変更することはほとんどありません。それでも、その違いは重要ではないかもしれません。プログラミング言語が暗黙のうちに 任意精度演算 に暗黙的に切り替えると、違いが見られる可能性が高くなります。
それでも、特定のコンパイラーが、気づいていない賢い最適化を実行している可能性があります。あなたが見つける方法は、測定することです。コードでプロファイラーを実行します。どの比較に最も時間がかかるかを確認します。または単にタイマーを開始および停止します。
多くのプロセッサには、比較を含む特定の即値オペランドに対して算術演算を実行できる「小さな」命令があります。これらの特別な値以外のオペランドは、より大きな命令フォーマットを使用するか、場合によっては「メモリから値をロード」命令を使用する必要があります。たとえば、ARM Cortex-M3命令セットでは、値を定数と比較する方法が少なくとも5つあります。
cmp r0,#1 ; One-Word instruction, limited to values 0-255
cmp r0,#1000 ; Two-Word instruction, limited to values 0-255 times a power of 2
cmn r0,#1000 ; Equivalent to comparing value with -1000
; Two-Word instruction, limited to values 0-255 times a power of 2
mov r1,#30000 ; Two words; can handle any value 0-65535
cmp r0,r1 ; Could use cmn to compare to values -1 to -65535
ldr r1,[constant1000000] ; One or two words, based upon how nearby the constant is
cmp r0,r1
...
constant1000000:
dd 1000000
最初の形式が最小です。 2番目と3番目の形式は、コードがフェッチされるメモリの速度に応じて、それほど速く実行されない場合があります。 4番目の形式は最初の3つよりもほぼ確実に遅くなり、5番目の形式はさらに遅くなりますが、後者は任意の32ビット値で使用できます。
古いx86プロセッサでは、短い形式の比較命令は長い形式の命令よりも速く実行されますが、多くの新しいプロセッサは、最初のフェッチ時に長い形式と短い形式の両方を同じ表現に変換し、その統一表現をキャッシュに格納します。したがって、組み込みコントローラー(多くのモバイルプラットフォームで見られるコントローラーなど)には速度の違いがありますが、多くのx86ベースのコンピューターにはありません。
また、定数がループ内で頻繁に使用される多くの場合、コンパイラーは定数をレジスターに1回(ループが始まる前に)ロードするだけで、タイミングの違いが表示されなくなります。一方、小さなループであっても、常にそうなるとは限らない状況もあります。ループは小さいが実行頻度が高い場合、短い即値を含む比較と長い値を含む比較との間で大きなパフォーマンスが発生することがあります。
この質問への短い答えは、noです。2つの数値がそれらに格納されていると仮定して、それらの数値の大きさに基づいて比較する時間差はありません。同じデータ型(たとえば、両方が32ビット整数または両方が64ビット長整数)
さらに、 [〜#〜] alu [〜#〜] のワードサイズまでは、2つの整数を互いに比較するのに1クロックサイクル以上かかることは信じられないほど信じられています。減算と同等の簡単な演算。私がこれまで扱ってきたすべてのアーキテクチャには、単一サイクルの整数比較があったと思います。
2つの数値の比較が単一サイクルの演算ではなかったと私が遭遇した唯一のケースは、次のとおりです。
@ RobertHarveyの回答 は良いです。この答えを彼の補足と考えてください。
分岐予測 も考慮する必要があります。
コンピュータアーキテクチャでは、分岐予測子は、分岐が確実に知られる前に分岐(たとえば、if-then-else構造)がどの方向に進むかを推測するデジタル回路です。分岐予測子の目的は、命令パイプラインのフローを改善することです。分岐予測子は、x86などの多くの最新のパイプライン化されたマイクロプロセッサアーキテクチャで高い効果的なパフォーマンスを実現する上で重要な役割を果たします。
基本的に、あなたの例では、ループ内のif
ステートメントが常に同じ答えを返す場合、システムは分岐する方向を正しく推測することで最適化できます。あなたの例では、最初のケースのif
ステートメントは常に同じ結果を返すため、2番目のケースよりもわずかに速く実行されます。
実装によって異なりますが、非常にありそうもありません。
私は、さまざまなブラウザエンジンの実装の詳細を読んだことがなく、CSSが数値の特定のタイプのストレージを指定していないことを認めます。しかし、主要なブラウザーのすべてが64ビットの倍精度浮動小数点数(「doubles」、C/C++から用語を借用する)を使用して、CSSでの数値のニーズのほとんどを処理していると想定するのは安全だと思いますこれはJavaScriptが数値に使用するものであり、同じ型を使用すると統合が容易になるためです。
コンピューターの観点から、すべてのdoubleは同じ量のデータを運びます:値が1か-3.14か、1000000か1e100かにかかわらず、64ビット。これらの数値を操作するのにかかる時間は、常に同じ量のデータを処理するため、これらの数値の実際の値には依存しません。 doubleはすべての数値(またはその範囲内のすべての数値)を正確に表すことができないという点で、この方法で行うことにはトレードオフがありますが、それらはほとんどの問題に対して十分に近づく可能性があり、CSSが行う種類の数値ではありません-それ以上の精度を必要とするのに十分な要求。これとJavaScriptとの完全な互換性の利点を組み合わせると、倍精度浮動小数点数のかなり強力なケースが得られます。
誰かが可変長エンコーディングを使用して数値にCSSを実装することは不可能ではありません。 誰かが可変長エンコーディングを使用している場合、その後小さい数と比較することは、大きい数を比較することよりもコストが低くなります。処理するデータが増えます。これらの種類のエンコーディングは、バイナリよりも正確である可能性がありますが、速度もはるかに遅く、特にCSSの場合、精度の向上は、パフォーマンスヒットに値するほど十分ではありません。どのブラウザでもこのように動作したことを知って、私は非常に驚かれます。
理論的には、上で述べたことすべてに1つの例外があります。ゼロと比較することは、他の数値と比較するよりも速いことが多いです。これは、ゼロが短いからではありません(それが理由である場合、1も同じくらい高速でなければなりませんが、そうではありません)。それはゼロがあなたをだますのを許すからです。これは、すべてのビットがオフになっている唯一の数値であるため、値の1つがゼロであることがわかっている場合は、他の値を数値として見る必要さえありません。ゼロの場合は、1ビットを見て、ゼロより大きいか小さいかを確認するだけです。
このコードが実行されるたびに解釈されていた場合、10000000000000
と比較して1000
をトークン化して解釈するのに時間がかかるため、違いがあります。ただし、これはこの場合のインタープリターの明らかな最初の最適化です。一度トークン化し、トークンを解釈します。