なぜ最新のプロセッサでの加算はビット単位の操作と同じくらい速いのですか?


73

最新のプロセッサではビット単位の操作が非常に高速であることがわかっています。32ビットまたは64ビットを並列で操作できるため、ビット単位の操作には1クロックサイクルしかかかりません。ただし、加算は、少なくとも1つ、場合によっては最大12個のビット単位の操作で構成される複雑な操作であるため、当然、3〜4倍遅くなると考えました。単純なベンチマークの後、ビット単位の演算(XOR、OR、ANDなど)のどれとでも加算が正確に速いことを見て驚いた。誰もこれに光を当てることができますか?




1
はい、私のテストでも乗算はかなり高速でした。加算は加算よりも約2倍遅く、除算は約30倍(!)倍遅くなりました。
ソロナサス

最先端の並列プレフィックスツリー加算器のコンパクト概要:デビッド・ハリスパラレルプリフィックスネットワークの分類:pages.hmc.edu/harris/research/taxonomy.pdf
Franki

もっと詳しく説明:博士号6月陳氏の博士論文「パラレル・プレフィックス構造をバイナリとモジュロ{2N-1、2N、2N + 1}のための加算器」digital.library.okstate.edu/etd/Chen_okstate_0664D_10070.pdf
Franki

回答:


104

CPU設計者が高速化に必要な回路を追加したため、追加は高速です。ビット単位の操作よりもかなり多くのゲートが必要ですが、CPU設計者がそれを価値があると判断するのに十分な頻度です。https://en.wikipedia.org/wiki/Adder_(electronics)を参照してください

どちらも、単一のCPUサイクル内で実行するのに十分な速さにすることができます。これらは同等に高速ではありません-追加にはビット単位の操作よりも多くのゲートと遅延が必要ですが、プロセッサが1クロックサイクルで実行できるほど十分に高速です。命令のデコードおよび制御ロジックには命令ごとのレイテンシオーバーヘッドがあり、そのレイテンシはビット単位の操作を行うレイテンシよりも大幅に大きいため、2つの間の差はそのオーバーヘッドによって圧倒されます。 AProgrammerの答えPaul92の答えは、これらの効果をよく説明しています。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
DW

38

いくつかの側面があります。

  • ビット演算と加算の相対的なコスト。単純な加算器は、単語の幅に線形に依存するゲート深さを持ちます。ゲートの観点からよりコストのかかる代替アプローチがあり、深さを減らします(IIRCの深さは、単語の幅の対数に依存します)。他の人はそのような技術の参考文献を与えていますが、遅延を追加する制御ロジックが必要なため、操作のコストを考えると、違いもそれほど重要ではないことを指摘します。

  • 次に、プロセッサには通常クロックが使用されているという事実があります(一部の研究や特別な目的の非クロック設計は知っていますが、市販されているものもありません)。これは、操作の速度が何であれ、クロックサイクルの整数倍でかかることを意味します。

  • 最後に、マイクロアーキテクチャーに関する考慮事項があります。必要なものを測定しますか?現在、プロセッサは、アウトオブオーダー実行など、パイプライン化されたマルチスカラーである傾向があります。これは、完了のさまざまな段階で、複数の命令を同時に実行できることを意味します。ある操作が他の操作よりも時間がかかることを測定値で示したい場合は、その違いを隠すことが目標であるため、これらの側面を考慮する必要があります。独立したデータを使用する場合、加算演算とビット演算のスループットが同じになる可能性が非常に高くなりますが、レイテンシの測定値または演算間の依存関係の導入はそうでない場合があります。また、メジャーのボトルネックは、たとえばメモリアクセスではなく実行にあることを確認する必要があります。


6
+1。はい、ほとんどのプロセッサにはクロックが供給されていますが、いくつかのクロックレスCPUが市販されています。
デビッドケーリー

2
別の可能性は、プロセッサが64ビットのレジスタを1つの16ビットピースと3つの17ビットピースとして格納し、各ピースの追加ビットが下からの繰り越しキャリーを保持することです。ビット単位演算またはストアが後に続く加算では、キャリーを伝播するために1〜2余分のサイクルが必要になる場合がありますが、別の加算が後に続く加算では必要ありません。さらに、「ストア」の場合、余分な伝搬時間がストアのパフォーマンスを遅らせる可能性がありますが、コードを「待機」する必要はありません。
スーパーキャット

3
@supercat Pentium 4は、上半分のビットの半サイクル前の後続の操作のために下位16ビットまたは32ビットを準備する倍速(プロセッサの残りの部分に対して)ALUでこのようなことを行いました。
ジェフリーボスブーム

2
あなたが望むものを測定していると確信していますか? この場合、測定値からのOPの結論は、大部分のCPUに対して正しいものです。加算は非常に一般的であるため、スーパースカラーCPUはすべての実行ポートにユニットを追加し、ブール値は(トランジスタ数で)実装するのに非常に安価なので、すべてのポートに存在します。したがって、ほとんどの場合、addとbooleanは同じスループット(Intel Haswellではクロックごとに4など)になります。
ピーターコーデス

2
ただし、SIMD整数加算は、通常、同じレイテンシーであるにもかかわらず、SIMDブール演算よりもスループットが低いことがよくあります。PentiumIIからBroadwellまでのIntel CPUは、paddwクロックごとに2でvector-int加算(たとえば)しか実行できませんが、pandクロックごとに3でブール値(など)を実行できます。(Skylakeは、3つのベクトル実行ポートすべてにベクトル加算器を配置します。)
Peter Cordes、

24

CPUはサイクルで動作します。各サイクルで、何かが起こります。通常、1つの命令を実行するにはより多くのサイクルが必要ですが、複数の命令が異なる状態で同時に実行されます。

たとえば、単純なプロセッサでは、命令ごとにフェッチ、実行、保存の3つのステップがあります。常に3つの命令が処理されています。1つはフェッチされ、1つは実行され、1つはその結果を保存します。これはパイプラインと呼ばれ、この例では3つのステージがあります。最新のプロセッサには、15以上のステージを持つパイプラインがあります。ただし、加算とほとんどの算術演算は通常1段階で実行されます(命令自体ではなく、ALUによる2つの数値の加算演算について話します-プロセッサアーキテクチャによっては、命令に必要な場合がありますメモリから引数を取得し、条件を実行し、結果をメモリに保存するためのサイクルが増えます)。

サイクルの期間は、最も長いクリティカルパスによって決まります。基本的に、ピップラインのある段階が完了するのに必要な最長時間です。CPUをより高速にしたい場合は、クリティカルパスを最適化する必要があります。クリティカルパス自体を減らすことができない場合、パイプラインの2ステージに分割でき、CPUをほぼ2倍の周波数でクロックできるようになります(これを防ぐことができるクリティカルパスが他にない場合) )。ただし、これにはオーバーヘッドが伴います。パイプラインのステージ間にレジスタを挿入する必要があります。つまり、実際には2倍の速度は得られず(レジスターはデータを保存するのに時間が必要です)、設計全体が複雑になります。

加算を実行するための非常に効率的な方法(キャリールックアヘッド加算器など)がすでにあり、加算はプロセッサ速度のクリティカルパスではないため、複数のサイクルに分割する意味はありません。

また、複雑に思えるかもしれませんが、ハードウェアでは並行して非常に高速に処理できることに注意してください。


3
長いパイプラインの大きなオーバーヘッドは、分岐の予測ミスから回復するためのサイクルが増えることです!最近では、ステージ間でデータをバッファリングするためにトランジスタを使用することはわずかです。単純なパイプライン化されたCPUでさえ、実際に実行されている命令より先にフェッチ/デコードする必要があります。分岐が予測とは異なる方法で行われたため(または他の何らかの推測ミス)、フロントエンドが間違ったコードで動作していることをCPUが検出した場合、その動作を破棄して正しい命令から開始する必要があります。事態が悪化するのは、飛行中に多くのinsnを持つことができるスーパースカラーの異常CPUです。
ピーターコーデス

12

プロセッサにはクロックが供給されているため、一部の命令が他の命令よりも明らかに高速に実行できる場合でも、同じサイクル数を消費する可能性があります。

レジスタと実行ユニット間でデータを転送するために必要な回路は、加算器よりもはるかに複雑であることがわかるでしょう。

単純なMOV(レジスタからレジスタ)命令はビット単位のロジックよりも計算量が少なくなりますが、MOVとADDは通常1サイクルかかります。MOVを2倍の速度で作成できる場合、CPUは2倍の速度でクロックされ、ADDは2サイクルになります。


コメントは詳細なディスカッション用ではありません。この会話はチャットに移動さました
ジル

1
議論の要約:いくつかの異常なCPUは、レジスタ名を変更してMOVを特別に処理し、実質的にレイテンシをゼロにします。x86のMOVを本当に「無料」にすることできますか?なぜこれをまったく再現できないのですか?MOVの実際のコストの詳細については。
ピーターコーデス

12

加算は、キャリービットが64ビットアキュムレータをリップルするのを待たないように十分に重要です。その用語はキャリールックアヘッド加算器であり、基本的には8ビットCPU(およびそのALU)以上の一部です。実際、最新のプロセッサーは完全な乗算にもそれほど多くの実行時間を必要としない傾向があります。キャリールックアヘッドは、実際にはプロセッサー設計者のツールボックスにある非常に古い(そして比較的手頃な)ツールです。


整数乗算は、x86でのADDよりも確実に遅延が大きく、スループットが低くなります。しかし、高速乗算器を構築するのに必要な加算器の数を考えると、驚くほど高速です。たとえば、Nehalem以降のIntelやRyzen以降のAMDでは、8/16/32/64ビットスカラー整数乗算は3サイクルレイテンシで、1cスループットあたり1つです。 (1つの完全にパイプライン化された実行ユニット)。これは、クロックあたり3または4のADDスループットと比較してひどいですが、Intel Pentium P5の9サイクルのIMULレイテンシと比較すると驚くべきものです。SIMDの場合も同様です。vector-int乗算は、addよりもレイテンシが高く、スループットが低くなりますが、それでも高速です。
ピーターコーデス

そのため、以前は、multiplyは現在よりも他の命令に比べてはるかに高価でした。通常、2命令を超えるコストでそれを回避する価値はありません。また、2命令の代替でさえ価値がない場合があります(たとえば、shift + add lea命令を使用する場合)。
ピーター・コーデス

9

ビット単位演算よりも多くのサイクルを必要とする追加機能を備えたプロセッサーを見つけるのは難しいと思います。ほとんどのプロセッサは、プログラムカウンタをインクリメントするために、命令サイクルごとに少なくとも1回の加算を実行する必要があるためです。単なるビット単位の操作はそれほど便利ではありません。

(クロックサイクルではなく命令サイクル-たとえば、6502はパイプライン化されておらず命令キャッシュがないため、命令ごとに最低2クロックサイクルかかります)

欠けている本当の概念は、クリティカルパスの概念です。チップ内では、1サイクル内で実行できる最長の操作が、ハードウェアレベルで、チップのクロック速度を決定します。

これの例外は(まれに使用され、ほとんど商業化されていない)非同期ロジックです。これは、ロジック伝搬時間、デバイス温度などに応じて異なる速度で実際に実行されます。


ユーザーが制御可能なビット単位の操作ではありませんが、8086での一部の命令(たとえば、割り込みフラグのクリア)は、整数の加算よりも少ないサイクルで済みました。より抽象的には、すべての命令のサイズが1ワードであるRISCシステムは、PC用の単純なバイナリカウンターを使用できます。これは、汎用加算器よりもはるかに高速な回路です。
マーク

オペランドの一方が小さいためプログラムカウンタに加算は、加算演算命令に比べて非常に簡単になる傾向がある(どちらかの命令サイズ、またはサイズも制限されているオフセット相対ジャンプ)
ベンフォークト

6502はパイプライン化されました-前の命令の最後のサイクルで次の命令の最初のバイトを読み取りました。そうでなければ、フェッチ/デコード/実行は少なくとも3サイクルでした。
gnasher729

8

ゲートレベルでは、追加するのにより多くの作業が必要であるため、時間がかかります。ただし、そのコストは問題にならないほど十分に些細なものです。

最新のプロセッサにはクロックが供給されています。このクロックレートの倍数以外では命令を実行できません。クロックレートを高くした場合、ビット単位操作の速度を最大化するには、追加に少なくとも2サイクルを費やす必要があります。2サイクル分の時間を実際に必要としないため、この時間の多くは待機することに費やされます。1.1(またはそのような数字)だけが必要でした。チップの追加は、市場の他の誰よりも遅くなりました。

さらに悪いことに、ビット単位の演算を追加または実行するという単なる行為は、サイクル中に起こっていることのほんの一部にすぎません。サイクル内で命令をフェッチ/デコードできる必要があります。サイクル内でキャッシュ操作を実行できる必要があります。単純な加算またはビット演算と同じタイムスケールで、他の多くのことが行われています。

もちろん、解決策は、非常に深いパイプラインを開発し、これらのタスクをビット単位の操作で定義される小さなサイクル時間に収まる小さな部分に分割することです。Pentium 4は、これらの深いパイプライン用語で思考の限界を有名に示しました。あらゆる種類の問題が発生します。特に、分岐が必要になるのは、データを取得したらパイプラインをフラッシュする必要があるため、分岐が難しくなることで有名です。


7

最新のプロセッサにはクロックが供給されています。すべての操作には、整数倍のクロックサイクルが必要です。プロセッサの設計者は、クロックサイクルの長さを決定します。ここには2つの考慮事項があります。1つは、ハードウェアの速度です。たとえば、単一のNANDゲートの遅延として測定されます。これは、使用されるテクノロジー、および速度と電力使用量のようなトレードオフに依存します。プロセッサの設計とは無関係です。2つ目に、設計者は、クロックサイクルの長さが単一のNANDゲートのn遅延に等しいと決定します。nは10、30、またはその他の値です。

この選択nは、1サイクルで処理できる複雑な操作の方法を制限します。15のNAND遅延では実行できないが、16で実行できる操作があります。したがって、n = 16を選択すると、そのような操作をサイクルで実行できることを意味し、n = 15を選択すると、実行できないことを意味します。

設計者はnを選択して、多くの重要な操作を1サイクル、あるいは2サイクルまたは3サイクルで実行できるようにします。nはローカルに最適に選択されます。nをn-1に置き換えた場合、ほとんどの操作は少し速くなりますが、一部(実際にn個のNAND遅延を完全に必要とする操作)は遅くなります。全体のプログラム実行が平均的に速くなるように、操作が遅くなることが少ない場合は、n-1を選択します。n + 1を選択することもできます。これにより、ほとんどの操作が少し遅くなりますが、n遅延内で実行できないがn + 1遅延内で実行できる多くの操作がある場合、プロセッサ全体が高速になります。

ここであなたの質問:加算と減算は非常に一般的な操作なので、1サイクルで実行できるようにしたいです。その結果、AND、ORなどがより高速に実行できることは重要ではありません。それらはまだその1サイクルを必要とします。もちろん、AND、ORなどを「計算」するユニットは、親指をいじるのに多くの時間がありますが、それは仕方がありません。

操作がn個のNAND遅延内で実行できるかどうかだけではないことに注意してください:たとえば、少し賢いことで追加を高速化でき、非常に賢いことでさらに高速にできます。 、最後に、プロセッサは非常に高速で非常に高価な回路と少し遅くて安価な回路を混在させることができるため、より多くのお金を費やすことで1つの操作を十分に速くすることができます。

今、あなたは可能性がクロック速度がそれほど高く作る/これだけの単純なビット演算を1サイクルで実行することを短く、二つ以上で他のすべてのサイクル。それはおそらくプロセッサの速度を低下させるでしょう。2サイクルかかる操作では、通常、不完全な命令をあるサイクルから次のサイクルに移動するためのオーバーヘッドがあるため、2サイクルでは実行に2倍の時間がかかることを意味しません。したがって、2サイクルで加算を行うには、クロック速度を2倍にすることはできません。


6

既存の回答で明示的に言及されていないいくつかのことを修正しましょう。

最新のプロセッサではビット単位の演算が非常に高速であることを知っています。32ビットまたは64ビットで並列に演算できるためです。

これは本当です。CPUを「XX」ビットとしてラベル付けすることは、通常(常にではない)、その一般的な構造(レジスタ幅、アドレス指定可能なRAMなど)のほとんどがXXビットのサイズ(多くの場合「+/- 1」など)であることを意味します。しかし、質問に関しては、32ビットまたは64ビットのCPUが32ビットまたは64ビットで基本的なビット操作を一定時間で行うと安全に仮定できます。

したがって、ビット単位の操作には1クロックサイクルしかかかりません。

この結論は必ずしも当てはまりません。特に、豊富な命令セット(google CISCとRISC)を備えたCPUは、単純なコマンドでも簡単に1サイクル以上かかることがあります。インターリーブを使用すると、単純なコマンドでも3クロックのfetch-exec-storeに分割される場合があります(例)。

ただし、追加は複雑な操作です

いいえ、整数の加算は簡単な操作です。同様に減算。加算器を完全なハードウェアに実装するのは非常に簡単であり、基本的なビット操作と同じように瞬時に処理を行います。

これは、少なくとも1つ、場合によっては最大12個のビット単位の操作で構成されているため、当然3〜4倍遅くなると思いました。

トランジスタを3〜4倍使用しますが、無視できる全体像と比較してください。

単純なベンチマークの後、ビット単位の演算(XOR、OR、ANDなど)とまったく同じように加算が高速であることに驚きました。誰もこれに光を当てることができますか?

はい:整数の加算ビット単位の演算です(他のビットよりも数ビット多いですが、それでも)。段階的に何もする必要はありません。複雑なアルゴリズム、クロック、その他のことは必要ありません。

CPUアーキテクチャよりも多くのビットを追加する場合は、段階的に実行する必要があるというペナルティが発生します。ただし、これは別のレベルの複雑さです(アセンブリ/マシンコードレベルではなく、プログラミング言語レベル)。これは、過去(または今日の小さな組み込みCPUでの)共通の問題でした。PCなどの場合、32ビットまたは64ビットで、最も一般的なデータ型でこれが問題点になり始めるのに十分です。


O(N)からO(sqrt(N))への追加の時間コストを削減しても、必要なトランジスタ数やルーティングの複雑さを大幅に増加させることはないことに注意するのは興味深いことです、およびsqrt(N)の追加のマージステージが必要ですO(lgN)トランジスタのコストで時間コストをO(lgN)に削減できますが、多くの場合、64たとえば、8つの8ビット加算(sqrtNフォワーディングを使用)を3層のマージロジックと結合するビット加算
。641

ええ、加算器はかなり単純です。本当に印象的なのは、完全にパイプライン化された3サイクルレイテンシの64ビット整数乗算器を備えた最新のx86 CPU です。(例:imul rax, rcxレイテンシー3c、Intel Sandybridgeファミリー、AMD Ryzenでのスループット1cに1つ)。64ビットの完全乗算(rdx:raxで128ビットの結果を生成する)でも、待ち時間とスループットは同じですが、2つのuop(異なるポートで並列に実行)として実装されます。(手順表と優れたマイクロアーチガイドについては、agner.org / optimizeを参照してください)。
ピーター・コルド

[add-with-carry]は別のレベルの複雑さです(アセンブリ/マシンコードレベルではなく、プログラミング言語レベルです。言語に依存します。16ビットCPUを対象とするACコンパイラは、コンパイル時にadd / adcを発行する必要があります。 2つのuint32_t値の追加。これは現在でも32ビットターゲットのint64_tに関連していますAVRは8ビットRISCマイクロコントローラーなので、32ビット整数には4つの命令が必要です:godbolt.org/g/wre0fM
Peter Cordes

はい、@ PeterCordes、それは私が意図したことです、私は私の文章を少し明確にしました。
AnoE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.