112

私はいくつかの SO アーカイブを読んでおり、x86 アーキテクチャに対するステートメントに遭遇しました。

などの多くのコメント

検索してみましたが、理由が見つかりませんでした。x86 が悪いとは思わないのは、おそらく x86 が私がよく知っている唯一のアーキテクチャだからです。

他の人に比べてx86が醜い/悪い/劣っていると考える理由を教えてください。

4

10 に答える 10

100

考えられるいくつかの理由:

  1. x86 は比較的古いISAです(その祖先は結局 8086 でした)。
  2. x86 は何度か大幅に進化しましたが、古いバイナリとの下位互換性を維持するにはハードウェアが必要です。たとえば、最新の x86 ハードウェアには、16 ビット コードをネイティブに実行するためのサポートがまだ含まれています。さらに、リアル モード、プロテクト モード、仮想 8086 モード、(amd64) ロング モードなど、古いコードを同じプロセッサ上で相互運用できるようにするためのメモリ アドレッシング モデルがいくつか存在します。これは、一部の人にとっては混乱を招く可能性があります。
  3. x86 は CISC マシンです。長い間、これは MIPS や ARM などの RISC マシンよりも遅いことを意味していました。これは、命令にはデータの相互依存性とフラグがあり、ほとんどの形式の命令レベルの並列処理を実装するのが難しいためです。最新の実装では、x86 命令を内部で「マイクロオペレーション」と呼ばれる RISC に似た命令に変換し、これらの種類の最適化をハードウェアに実装する実用的なものにしています。
  4. いくつかの点で、x86 は劣っているわけではなく、単に異なるだけです。たとえば、大多数のアーキテクチャでは入出力はメモリ マッピングとして処理されますが、x86 では処理されません。(注意: 最近の x86 マシンは通常、何らかの形式のDMAサポートを備えており、メモリ マッピングを介して他のハードウェアと通信します。ただし、ISAINにはやのような I/O 命令がまだありますOUT)
  5. x86 ISAには非常に少数のアーキテクチャ レジスタがあり、プログラムが必要以上に頻繁にメモリを往復するように強制できます。これを行うために必要な追加の命令は、効率的なストア フォワーディングではあるものの、有用な作業に費やすことができる実行リソースを必要とします。レイテンシーを低く保ちます。大規模な物理レジスタ ファイルにレジスタの名前を変更する最新の実装では、多くの命令を処理中のままにしておくことができますが、アーキテクチャ レジスタの欠如は依然として 32 ビット x86 の重大な弱点でした。x86-64 の 8 から 16 への整数およびベクトル レジスタの増加は、各レジスタの幅の増加ではなく、64 ビット コードが 32 ビットよりも高速である最大の要因の 1 つです (より効率的なレジスタ呼び出し ABI と共に)。整数レジスタを 16 から 32 にさらに増やすと、多少は効果がありますが、それほどではありません。(ただし、AVX512 は 32 個のベクトル レジスタに増加します。これは、浮動小数点コードのレイテンシが高く、多くの場合、より多くの定数が必要になるためです。) (コメントを参照)
  6. x86 は多くの機能を備えた複雑なアーキテクチャであるため、x86 アセンブリ コードは複雑です。典型的な MIPS マシンの命令リストは、1 枚のレター サイズの紙に収まります。x86 の同等のリストは数ページを占めますが、手順はより多くのことを行うため、リストが提供できるよりも詳細な説明が必要になることがよくあります。たとえば、MOVSB命令が何を行うかを記述するには、C コードの比較的大きなブロックが必要です。

    if (DF==0) 
      *(byte*)DI++ = *(byte*)SI++; 
    else 
      *(byte*)DI-- = *(byte*)SI--;
    

    これは、ロード、ストア、および 2 つの加算または減算 (フラグ入力によって制御される) を実行する単一の命令であり、それぞれが RISC マシンでは個別の命令になります。

    MIPS (および同様のアーキテクチャ) の単純さは必ずしもそれらを優れたものにするわけではありませんが、アセンブラ クラスの紹介を教えるには、より単純なISAから始めるのが理にかなっています。一部のアセンブリ クラスでは、 y86と呼ばれる x86 の超単純化されたサブセットを教えています。これは、実際の使用には役に立たないという点を超えて単純化されています (たとえば、シフト命令がない)、または基本的な x86 命令だけを教えるクラスもあります。

  7. x86 は可変長オペコードを使用するため、命令の解析に関してハードウェアが複雑になります。現代では、CPU が生の計算よりもメモリ帯域幅によってますます制限されるようになるため、このコストは無視できるほど小さくなっていますが、多くの「x86 バッシング」に関する記事や態度は、このコストが比較的はるかに大きかった時代に由来しています。
    2016 年の更新: Anandtech は、x64 および AArch64 でのオペコード サイズに関するディスカッションを投稿しました。

編集: これはx86 の bash ではありません! パーティ。質問の言い回しを考えると、いくらかのバッシングをするしかありませんでした。ただし、(1) を除いて、これらすべてのことは正当な理由で行われました (コメントを参照)。インテルの設計者は愚かではありません。彼らは自分たちのアーキテクチャーで何かを達成したかったのです。これらは、それらを実現するために支払わなければならなかった税金の一部です。

于 2010-04-21T02:26:26.640 に答える
29

私の考えでは、x86 に対する主な欠点は、その CISC の起源です。命令セットには、多くの暗黙の相互依存関係が含まれています。これらの相互依存性により、チップ上で命令の並べ替えなどを行うことが困難になります。これは、これらの相互依存性のアーティファクトとセマンティクスを各命令に対して保持する必要があるためです。

たとえば、ほとんどの x86 整数加算および減算命令は、フラグ レジスタを変更します。加算または減算を実行した後、次の操作では、多くの場合、フラグ レジスタを調べてオーバーフローや符号ビットなどをチェックします。その後に別の加算がある場合、2 番目の加算の実行を安全に開始できるかどうかを判断するのは非常に困難です。最初の追加の結果がわかる前に。

RISC アーキテクチャでは、add 命令は入力オペランドと出力レジスタを指定し、操作に関するすべてはそれらのレジスタのみを使用して行われます。これにより、すべてを並べて単一のファイルを実行するように強制するブルーミン フラグ レジスタがないため、互いに近い追加操作を分離することがはるかに簡単になります。

MIPS スタイルの RISC 設計である DEC Alpha AXP チップは、使用可能な命令が非常に質素でしたが、命令セットは、命令間の暗黙的なレジスタ依存性を回避するように設計されていました。ハードウェア定義のスタック レジスタはありませんでした。ハードウェア定義のフラグ レジスタはありませんでした。命令ポインターでさえ OS で定義されていました。呼び出し元に戻りたい場合は、呼び出し元がどのアドレスに戻るかを知らせる方法を考え出す必要がありました。これは通常、OS 呼び出し規則によって定義されていました。ただし、x86 では、チップ ハードウェアによって定義されます。

とにかく、Alpha AXP チップ設計の 3 世代または 4 世代にわたって、ハードウェアは、32 個の int レジスタと 32 個の float レジスタを備えた質素な命令セットの文字通りの実装から、80 個の内部レジスタ、レジスタの名前変更、結果の転送 (前の命令の結果が値に依存する後の命令に転送される場合) およびあらゆる種類のワイルドでクレイジーなパフォーマンス ブースター。そして、これらすべての機能を備えた AXP チップ ダイは、当時の同等の Pentium チップ ダイよりもかなり小さく、AXP は非常に高速でした。

x86 ファミリ ツリーでは、こうした種類のパフォーマンス ブーストは見られません。これは主に、x86 命令セットの複雑さが、不可能ではないにしても、多くの種類の実行最適化を法外に高価にするためです。Intel の才能は、x86 命令セットをハードウェアに実装することをあきらめたことにありました。最新の x86 チップはすべて、実際には RISC コアであり、x86 命令をある程度解釈し、元の x86 のすべてのセマンティクスを保持する内部マイクロコードに変換します。命令ですが、その RISC アウトオブオーダーおよびマイクロコードに対するその他の最適化を少し可能にします。

私は多くの x86 アセンブラーを作成しており、その CISC ルーツの利便性を十分に理解できます。しかし、Alpha AXP アセンブラーを作成するまで、x86 がどれほど複雑であるかを十分に理解していませんでした。私は、AXP のシンプルさと統一性に度肝を抜かれました。その違いは計り知れず、深刻です。

于 2010-04-21T03:04:21.130 に答える
21

x86 アーキテクチャは、8008 マイクロプロセッサおよびその関連製品の設計にまでさかのぼります。これらの CPU は、メモリが低速だった時代に設計されたものであり、CPU ダイで実行できれば、多くの場合、はるかに高速でした。ただし、CPU のダイスペースも高価でした。これらの 2 つの理由により、特殊な目的を持つ傾向がある少数のレジスタしか存在せず、あらゆる種類の落とし穴と制限を伴う複雑な命令セットしか存在しません。

同じ時代の他のプロセッサ (6502 ファミリなど) にも同様の制限と癖があります。興味深いことに、8008 シリーズと 6502 シリーズの両方が組み込みコントローラーとして意図されていました。当時でさえ、組み込みコントローラーはアセンブラーでプログラムされることが期待されており、多くの点で、コンパイラー・ライターではなくアセンブリー・プログラマー向けでした。(コンパイラの書き込みに応じたときに何が起こるかについては、VAX チップを見てください。) 設計者は、それらが汎用コンピューティング プラットフォームになるとは予想していませんでした。それが、POWER アーキテクチャーの前身のようなものでした。もちろん、ホームコンピュータ革命はそれを変えました。

于 2010-04-21T03:03:38.907 に答える
13

ここにはいくつかの追加の側面があります。

操作「a=b/c」を考えてみましょう。x86 はこれを次のように実装します。

  mov eax,b
  xor edx,edx
  div dword ptr c
  mov a,eax

div 命令の追加ボーナスとして、edx には残りが含まれます。

RISC プロセッサでは、最初に b と c のアドレスをロードし、b と c をメモリからレジスタにロードし、除算を行って a のアドレスをロードし、結果を格納する必要があります。Dst,src 構文:

  mov r5,addr b
  mov r5,[r5]
  mov r6,addr c
  mov r6,[r6]
  div r7,r5,r6
  mov r5,addr a
  mov [r5],r7

ここでは通常、残りはありません。

ポインターを介して変数をロードする場合、両方のシーケンスが長くなる可能性がありますが、別のレジスターに 1 つ以上のポインターが既にロードされている可能性があるため、RISC の可能性は低くなります。x86 はレジスターが少ないため、ポインターがレジスターの 1 つに含まれる可能性は低くなります。

長所と短所:

RISC 命令は、命令スケジューリングを改善するために周囲のコードと混合される場合があります。これは、代わりに CPU 自体の内部でこの作業を行う (シーケンスに応じて多かれ少なかれうまくいく) x86 では可能性が低くなります。上記の RISC シーケンスは、通常、32 ビット アーキテクチャでは 28 バイト長 (それぞれ 32 ビット/4 バイト幅の 7 つの命令) になります。これにより、命令をフェッチするとき (7 回のフェッチ) にオフチップ メモリがより多く機能します。高密度の x86 シーケンスには含まれる命令が少なく、幅はさまざまですが、おそらく平均 4 バイト/命令も見られます。これを高速化する命令キャッシュがある場合でも、7 つのフェッチは、x86 と比較して他の場所で 3 つの不足を補う必要があることを意味します。

保存/復元するレジスタが少ない x86 アーキテクチャは、おそらく RISC よりも高速にスレッド スイッチを実行し、割り込みを処理することを意味します。保存および復元するレジスタが増えると、割り込みを行うためにより多くの一時的な RAM スタック スペースが必要になり、スレッドの状態を保存するためにより多くの永続的なスタック スペースが必要になります。これらの側面により、x86 は純粋な RTOS を実行するためのより良い候補になるはずです。

個人的な話ですが、RISC アセンブリを記述するのは x86 よりも難しいと思います。これを解決するには、RISC ルーチンを C で記述し、生成されたコードをコンパイルおよび変更します。これは、コード生成の観点からはより効率的ですが、実行の観点からはおそらく効率的ではありません。これら 32 のレジスタすべてを追跡します。x86 の場合は逆です。「実際の」名前を持つ 6 ~ 8 個のレジスタを使用すると、問題が管理しやすくなり、生成されたコードが期待どおりに機能するという確信が高まります。

醜い?それは見る人の目にある。私は「違う」ほうが好きです。

于 2010-12-07T10:00:16.863 に答える
11

x86 アセンブラー言語はそれほど悪くありません。マシンコードに到達すると、本当に醜くなり始めます。命令エンコーディング、アドレッシング モードなどは、ほとんどの RISC CPU のものよりもはるかに複雑です。また、下位互換性のために追加の楽しみが組み込まれています。プロセッサが特定の状態にある場合にのみ機能するものです。

たとえば、16 ビット モードでは、アドレッシングはまったく奇妙に見える場合があります。用のアドレッシング モードはありますが、用のモードは[BX+SI]ありません[AX+BX]。このようなことは、必要に応じて使用できるレジスターに値を確実に格納する必要があるため、レジスターの使用を複雑にする傾向があります。

(幸いなことに、32 ビット モードの方がはるかに健全です (ただし、セグメンテーションなど、それ自体が少し奇妙である場合もあります)。16 ビットの x86 コードは、ブート ローダーや一部の組み込み環境以外では、ほとんど無関係です。)

Intel が x86 を究極のプロセッサにしようとしていた昔の名残りもあります。数バイトの長さの命令は、実際にはもう誰も実行していないタスクを実行しました。2 つの例として、 ENTER 命令とLOOP 命令- C スタック フレーム コードは、ほとんどのコンパイラで "enter" ではなく "push ebp; mov ebp, esp" のようなものであることに注意してください。

于 2010-04-21T02:47:25.390 に答える
4

私は専門家ではありませんが、人々が好まない理由の多くは、パフォーマンスが優れている理由のようです。数年前、(スタックの代わりに) レジスター、レジスター フレームなどを使用することは、アーキテクチャを人間にとってよりシンプルに見せるための優れたソリューションと見なされていました。しかし、今日では重要なのはキャッシュのパフォーマンスであり、x86 の可変長ワードにより、より多くの命令をキャッシュに格納できます。対戦相手が指摘した「命令デコード」は、かつてチップの半分を占めていたと思いますが、もはやそれほどではありません。

並列処理は、今日では最も重要な要素の 1 つだと思います。少なくとも、使用できるほど高速に実行されるアルゴリズムにとっては。ソフトウェアで高度な並列処理を表現すると、ハードウェアでメモリのレイテンシを償却 (または完全に隠す) ことができます。もちろん、アーキテクチャの未来は、おそらく量子コンピューティングのようなものになるでしょう。

nVidia から聞いたことがありますが、Intel の過ちの 1 つは、バイナリ形式をハードウェアの近くに置いていたことです。CUDA の PTX は高速なレジスタ使用計算 (グラフの色付け) を実行するため、nVidia はスタック マシンの代わりにレジスタ マシンを使用できますが、古いソフトウェアをすべて壊すことのないアップグレード パスがあります。

于 2010-04-21T02:43:54.743 に答える
3

人々がすでに言及した理由に加えて:

  • x86-16にはかなり奇妙なメモリアドレス指定スキームがあり、単一のメモリ位置を最大4096の異なる方法でアドレス指定でき、RAMを1 MBに制限し、プログラマーに2つの異なるサイズのポインターを処理するように強制しました。幸い、32ビットへの移行によりこの機能は不要になりましたが、x86チップは依然としてセグメントレジスタの残骸を担っています。
  • x86自体の欠点ではありませんが、x86の呼び出し規約はMIPSのように標準化されておらず(主にMS-DOSにはコンパイラが付属していなかったため)、、、などの混乱が残り__cdeclました__stdcall__fastcall
于 2010-04-21T03:32:17.537 に答える
3

x86 をターゲットとするコンパイラを作成しようとする場合、または x86 マシン エミュレーターを作成する場合、またはハードウェア設計に ISA を実装しようとする場合でも、答えの一部が得られると思います。

「x86は醜い!」というのは分かりますが。私は今でも、MIPS よりも x86 アセンブリを書く方が楽しいと思います(たとえば)。後者は単純に面倒です。これは常に、人間ではなくコンパイラーに優しいものになるように意図されていました。試してみると、チップがコンパイラの作成者に対してより敵対的になる可能性があるかどうかはわかりません...

私にとって最も醜い部分は、(リアルモード) セグメンテーションの仕組みです。どの物理アドレスにも 4096 個のセグメント:オフセット エイリアスがあるということです。最後にそれが必要になったのはいつですか。セグメント部分が厳密に 32 ビット アドレスの上位ビットである場合、事態は非常に単純になります。

于 2010-05-14T16:41:15.213 に答える
2
  1. x86 の汎用レジスタのセットは非常に限られています

  2. 効率的なロード/ストア手法ではなく、最下位レベル (CISC 地獄) で非常に非効率的な開発スタイルを促進します。

  3. インテルは、時代遅れのテクノロジーとの互換性を維持するために、明らかにばかげたセグメント/オフセット - メモリ アドレッシング モデルを導入するという恐ろしい決定を下しました。

  4. 誰もが 32 ビットに移行していた時代に、x86 はわずか 16 ビットであるため、主流の PC の世界を妨げていました (それらのほとんど - 8088 - 8 ビットの外部データ パスしかなく、さらに恐ろしいことです!) CPU


私にとって (そして私は、開発者の観点からあらゆる世代の PC を見てきた DOS のベテランです!) ポイント 3. は最悪でした。

90 年代初頭 (主流!) の次の状況を想像してみてください。

a) レガシーな理由 (簡単にアクセスできる 640kB の RAM) のために非常識な制限があったオペレーティング システム - DOS

b) オペレーティング システムの拡張機能 (Windows) は、RAM に関してはより多くのことができますが、ゲームなどに関しては制限があり、地球上で最も安定したものではありませんでした (幸いなことに、これは後で変更されましたが、私はここでは90年代初頭について話している)

c) ほとんどのソフトウェアはまだ DOS であり、特別なソフトウェア用にブート ディスクを頻繁に作成する必要がありました。この EMM386.exe があったため、いくつかのプログラムは好きで、他のプログラムは嫌いでした (特にゲーマー - そして私は当時 AVID ゲーマーでした - 私が何を知っているかを知っています)。ここで話している)

d) MCGA 320x200x8 ビットに制限されていました (わかりました、特別なトリックでもう少しありました。360x480x8 は可能でしたが、ランタイム ライブラリのサポートなしでのみ)、他のすべては面倒で恐ろしいものでした ("VESA" - 笑)

e) しかし、ハードウェアに関しては、数メガバイトの RAM と最大 1024x768 をサポートする VGA カードを備えた 32 ビット マシンがありました。

この悪い状況の理由は?

インテルによる単純な設計上の決定。マシン命令レベル (バイナリ レベルではない!) の互換性は、すでに死にかけていたもので、おそらく 8085 だったと思います。他の、一見無関係な問題 (グラフィック モードなど...) は、技術的な理由と非常に狭い範囲で関連していました。 x86 プラットフォームがもたらした優れたアーキテクチャ。

現在は状況が異なりますが、x86 用のコンパイラ バックエンドを構築しているアセンブラ開発者または人に尋ねてください。汎用レジスタの数が非常に少ないことは、恐ろしいパフォーマンス キラーに他なりません。

于 2010-06-30T08:39:36.013 に答える