48

コードの特定のセクションを実際に最適化して高速化するために、プログラマーがそのセクションをアセンブリ言語で記述することをどこかで読んだことを覚えています。私の質問は -

  1. この練習は今でも行われていますか?どうやってこれを行うのですか?
  2. アセンブリ言語で書くのは少し面倒で古臭くありませんか?
  3. C コードを (-O3 フラグの有無にかかわらず) コンパイルすると、コンパイラはコードの最適化を行い、すべてのライブラリをリンクし、コードをバイナリ オブジェクト ファイルに変換します。したがって、プログラムを実行すると、プログラムはすでに最も基本的な形式、つまりバイナリになっています。では、「アセンブリ言語」を誘導することはどのように役立つのでしょうか?

私はこの概念を理解しようとしています。ヘルプやリンクは大歓迎です。

更新: dbemerlin の要求に応じてポイント 3 を言い換えます。コンパイラが生成するよりも効果的なアセンブリ コードを記述できる可能性がありますが、アセンブラの専門家でない限り、多くの場合、コンパイラはほとんどの人間よりもコードを最適化するため、コードの実行はおそらく遅くなります。 .

4

14 に答える 14

30

アセンブリ言語に戻すことが役立つのは、次の場合だけです。

  • CPU 命令には、C++ の同等の機能がありません (例: 単一命令複数データ命令、BCD または 10 進算術演算)。

    また

  • 何らかの不可解な理由 - オプティマイザが最適な CPU 命令を使用できていない

...と...

  • これらの CPU 命令を使用すると、ボトルネック コードのパフォーマンスが大幅に向上します。

単純にインライン アセンブリを使用して、C++ で簡単に表現できる操作 (2 つの値の追加や文字列の検索など) を行うことは、次の理由から逆効果です。

  • コンパイラはこれを同様にうまく行う方法を知っています
    • これを確認するには、アセンブリ出力 (例: gcc -S) を確認するか、マシン コードを逆アセンブルします。
  • レジスタ割り当て、CPU命令などに関する選択を人為的に制限しているため、ハードコードされた命令を実行するために必要な値でCPUレジスタを準備するのに時間がかかり、その後、将来の命令の最適な割り当てに戻るのに時間がかかる場合があります
    • コンパイラオプティマイザーは、異なるレジスターを指定する同等のパフォーマンスの命令から選択して、それらの間のコピーを最小限に抑えることができます。また、単一のコアが 1 サイクル中に複数の命令を処理できるようにレジスターを選択できますが、特定のレジスターを介してすべてを強制すると、それがシリアル化されます。
      • 公平を期すために、GCC には、CPU を正確なレジスタに制約することなく、特定のタイプのレジスタのニーズを表現する方法があり、そのような最適化を可能にしますが、これに対処するインライン アセンブリはこれまで見たことがありません。
  • 来年、同じ論理演算で 1000% 高速な別の命令を備えた新しい CPU モデルが登場した場合、コンパイラ ベンダーはその命令を使用するようにコンパイラを更新する可能性が高くなります。 (またはその時点でソフトウェアを保守している人)
  • コンパイラは、ターゲット アーキテクチャに最適なアプローチを選択します。1 つのソリューションをハードコードする場合は#ifdef、プラットフォームの最小公分母または -ed にする必要があります。
  • アセンブリ言語は、CPU とコンパイラの両方で C++ ほど移植性が高くありません。命令を移植したように見えても、上書きしても安全なレジスタや引数渡し規則などを誤って再登録する可能性があります。
  • 他のプログラマーはアセンブリを知らないか、慣れていない可能性があります

心に留めておく価値があると思う 1 つの視点は、C が導入されたとき、生成される機械語コードにこだわる多くの筋金入りのアセンブリ言語プログラマーを納得させる必要があったということです。当時、マシンの CPU パワーと RAM は少なく、人々は些細なことに夢中になっていたに違いありません。オプティマイザは非常に洗練され、改善を続けていますが、x86 などのプロセッサのアセンブリ言語は、実行パイプライン、キャッシュ、およびパフォーマンスに関与するその他の要因と同様に、ますます複雑になっています。命令ごとのサイクルのテーブルから値を追加することはできなくなりました。コンパイラの作成者は、これらすべての微妙な要因を考慮することに時間を費やします (特に、CPU メーカーで働いている人はそうですが、それは他のコンパイラへの圧力も高めます)。これ' アセンブリ プログラマーが、優れた最適化コンパイラによって生成されたコードよりも大幅に優れたコード効率を平均化することは現在では非現実的です。したがって、アセンブリの使用は、カップリングとメンテナンスのコストに見合う、測定可能で有用な違いが実際に生じる場合に限定する必要があります。

于 2010-11-17T09:22:40.250 に答える
14

まず、プログラムのプロファイルを作成する必要があります。次に、C または C++ コードで最もよく使用されるパスを最適化します。利点が明らかでない限り、アセンブラで書き直さないでください。アセンブラを使用すると、コードの保守が難しくなり、移植性が大幅に低下します。非常にまれな状況を除いて、使用する価値はありません。

于 2010-11-17T08:37:29.620 に答える
10

(1) はい、これを試す最も簡単な方法は、インライン アセンブリを使用することです。これはコンパイラに依存しますが、通常は次のようになります。

__asm
{
    mov eax, ebx
}

(2)これは非常に主観的です

(3) コンパイラが生成するよりも効果的なアセンブリ コードを記述できる可能性があるため。

于 2010-11-17T08:42:34.480 に答える
6

古典的な本Zen of Code OptimizationMichael AbrashZen of Graphics Programmingによる続編を読むべきです。

最初の本で要約すると、彼はアセンブリ プログラミングを限界まで使用する方法を説明しました。フォローアップで彼は、プログラマーはむしろ C のような高水準言語を使用し、必要に応じてアセンブリを使用して非常に特定のスポットのみを最適化しようとするべきであると説明しました。

この考え方の変化の動機の 1 つは、ある世代のプロセッサ用に高度に最適化されたプログラムが、高水準言語 (新しい命令を使用するコンパイラなど) からコンパイルされたコードと比較して、同じプロセッサ ファミリの次の世代では (多少) 遅くなる可能性があることを見たことです。たとえば、プロセッサの世代から別の世代への既存のプロセッサのパフォーマンスと動作の変更など)。

もう 1 つの理由は、コンパイラが非常に優れており、最近では積極的に最適化を行っているためです。通常、C コードをアセンブリに変換するアルゴリズムで作業するために、はるかに多くのパフォーマンスが得られます。GPU (グラフィック カード プロセッサ) プログラミングでも、cuda または OpenCL を使用して C で実行できます。

通常、ハードウェアを非常に細かく制御するために、アセンブリを使用する必要がある/使用しなければならない(まれな)ケースがまだいくつかあります。しかし、OS カーネル コードでさえ、通常は非常に小さな部分であり、それほど多くのコードではありません。

于 2010-11-17T09:04:51.207 に答える
4

プロセッサを指定したとは思いません。プロセッサと環境によって異なる答え。一般的な答えはイエスです。一般的な理由はコンパイラーです。コンパイラーは、一般的に最適化を行うことはできますが、特定のターゲットに対してはあまりうまくいかないことがあります。あるターゲットには非常に優れていて、他のターゲットにはあまり向いていない人もいます。ほとんどの場合、それで十分ですが、ほとんどの場合、移植性のないアセンブラではなく、移植性のある C コードが必要です。しかし、C ライブラリは memcpy やその他のルーチンを手動で最適化し、コンパイラはそれを実装するための非常に高速な方法があることを単に理解できないことがわかります。部分的には、そのコーナー ケースはコンパイラの最適化に時間を費やす価値がないため、アセンブラーでそれを解決するだけで、ビルドシステムには多くの場合、このターゲットがCを使用する場合、そのターゲットがCを使用する場合、そのターゲットがasmを使用する場合、そのターゲットがasmを使用する場合があります。そのため、それはまだ発生しており、一部の地域では永遠に続く必要があると私は主張します.

X86 は、多くの歴史を持つ独自の野獣です。実際には、常に高速なアセンブラーの 1 つの塊を実際に作成することはできません。特定のマシン上の特定のプロセッサのルーチンを確実に最適化できます。日、コンパイラを実行します。いくつかの特定のケースを除いて、それは一般的に無駄です。教育的ですが、全体的に時間の価値はありません。また、プロセッサはもはやボトルネックではないことに注意してください。そのため、ずさんな汎用 C コンパイラで十分です。他の場所でパフォーマンスを見つけてください。

多くの場合、組み込み、arm、mips、avr、msp430、pic などを意味する他のプラットフォーム。オペレーティング システムを実行している場合と実行していない場合があります。したがって、コンパイラの弱点が明らかになります。また、プログラミング言語は、プロセッサに向かってではなく、プロセッサから離れて進化し続けていることにも注意してください。おそらく低水準言語と思われるCの場合でも、命令セットとは一致しません。コンパイラよりも優れたアセンブラのセグメントを生成できる場合が常にあります。必ずしもボトルネックになっているセグメントではありませんが、プログラム全体を通して、あちこちで改善できることがよくあります。それを行うことの価値をまだ確認する必要があります。組み込み環境では、製品の成功と失敗を分ける可能性があり、実際にそうです。

真のエンベデッドは、専門のエンジニアがいる専門市場です。別の組み込み市場、組み込み Linux roku、tivo など。サード パーティの開発者が必要なため、組み込み電話などはすべて、生き残るために移植可能なオペレーティング システムが必要です。そのため、プラットフォームは組み込みシステムというよりはデスクトップに近いものでなければなりません。前述のように C ライブラリまたはオペレーティング システムに埋め込まれているため、アセンブラーの最適化が行われる可能性がありますが、デスクトップの場合と同様に、手動で最適化するのではなく、ソフトウェアを移植できるように、より多くのハードウェアを投入する必要があります。また、サードパーティの成功にアセンブラーが必要な場合、製品ラインまたは組み込みオペレーティング システムは失敗します。

私が持っている最大の懸念は、この知識が驚くべき速さで失われていることです. 誰もアセンブラを検査していないからです。誰もアセンブラで書いていないからです。生成されるコードに関しては、コンパイラが改善されていないことに誰も気づいていません。開発者は、コンパイラを知っているか、より良いプログラムを作成する方法を知っていれば、同じコンパイラで、場合によっては同じソース コードを使用して、パフォーマンスを 5 ~数百パーセント向上させることができることに気付かずに、ハードウェアを追加購入する必要があると考えることがよくあります。通常、同じソース コードとコンパイラで 5 ~ 10%。gcc 4 が常に gcc 3 より優れたコードを生成するとは限りません。ターゲット固有のコンパイラは (常に実行するとは限りません)、gcc の周囲で円を実行できます。同じソース コードを別のコンパイラで使用すると、数百パーセントの改善が見られる場合があります。このすべてはどこから来たのですか?まだアセンブラを調べたり使用したりするのに苦労している人々。それらの人々の何人かは、コンパイラのバックエンドで働いています。フロントエンドとミドルは確かに楽しくて教育的ですが、バックエンドは、結果として得られるプログラムの品質とパフォーマンスを左右する場所です。アセンブラを書いたことがなく、時々コンパイラからの出力 (gcc -O2 -s myprog.c) を見るだけでも、より優れた高レベルのプログラマになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。このすべてはどこから来たのですか?まだアセンブラを調べたり使用したりするのに苦労している人々。それらの人々の何人かは、コンパイラのバックエンドで働いています。フロントエンドとミドルは確かに楽しくて教育的ですが、バックエンドは、結果として得られるプログラムの品質とパフォーマンスを左右する場所です。アセンブラを書いたことがなく、時々コンパイラからの出力 (gcc -O2 -s myprog.c) を見るだけでも、より優れた高レベルのプログラマになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。このすべてはどこから来たのですか?まだアセンブラを調べたり使用したりするのに苦労している人々。それらの人々の何人かは、コンパイラのバックエンドで働いています。フロントエンドとミドルは確かに楽しくて教育的ですが、バックエンドは、結果として得られるプログラムの品質とパフォーマンスを左右する場所です。アセンブラを書いたことがなく、時々コンパイラからの出力 (gcc -O2 -s myprog.c) を見るだけでも、より優れた高レベルのプログラマになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。フロントエンドとミドルは確かに楽しくて教育的ですが、バックエンドは、結果として得られるプログラムの品質とパフォーマンスを左右する場所です。アセンブラを書いたことがなく、時々コンパイラからの出力 (gcc -O2 -s myprog.c) を見るだけでも、より優れた高レベルのプログラマになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。フロントエンドとミドルは確かに楽しくて教育的ですが、バックエンドは、結果として得られるプログラムの品質とパフォーマンスを左右する場所です。アセンブラを書いたことがなく、時々コンパイラからの出力 (gcc -O2 -s myprog.c) を見るだけでも、より優れた高レベルのプログラマになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。c) より高度なプログラマーになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。c) より高度なプログラマーになり、この知識の一部を保持できます。誰もアセンブラーを知り、書きたがらない場合、定義上、高水準言語用のコンパイラーの作成と保守をあきらめたことになり、ソフトウェアは一般に存在しなくなります。

たとえばgccの場合、コンパイラの出力はアセンブリであり、アセンブラに渡されてオブジェクトコードに変換されることを理解してください。通常、C コンパイラはバイナリを生成しません。オブジェクトは、最終的なバイナリに結合されると、リンカーによって実行されます。これは、コンパイラによって呼び出される別のプログラムであり、コンパイラの一部ではありません。コンパイラは、C、C++、ADA などをアセンブラに変換し、アセンブラとリンカ ツールが残りの部分を処理します。たとえば tcc のような動的再コンパイラーは、何らかの形でその場でバイナリーを生成できなければなりませんが、それは規則ではなく例外だと思います。LLVM には独自のランタイム ソリューションがあるだけでなく、クロス コンパイラとして使用する場合にバイナリ パスへのコードを対象とする内部コードの高レベルを非常に視覚的に示します。

要点に戻ると、はい、あなたが思っているよりも頻繁に行われています。ほとんどの場合、命令セットと直接比較しない言語に関係しており、コンパイラは常に十分な速度のコードを生成するとは限りません。malloc や memcpy などの頻繁に使用される関数の数十倍の改善を得ることができれば。または、ハードウェア サポートなしで携帯電話に HD ビデオ プレーヤーを搭載したい場合は、アセンブラーの長所と短所のバランスを取ります。真の組み込み市場では、依然としてアセンブラがかなり使用されています。すべて C である場合もあれば、ソフトウェアが完全にアセンブラでコーディングされている場合もあります。デスクトップ x86 の場合、プロセッサはボトルネックではありません。プロセッサはマイクロコード化されています。表面的に美しいアセンブラを作成したとしても、すべてのファミリの x86 プロセッサで非常に高速に実行されるわけではありません。

arm、thumb/thumb2、mips、msp430、avr などの x86 以外の ISA 用のアセンブラを学習することを強くお勧めします。コンパイラを持つターゲット、特に gcc または llvm コンパイラをサポートするもの。アセンブラーを学び、C コンパイラーの出力を理解することを学び、実際にその出力を変更してテストすることで、より良い結果が得られることを証明してください。この知識は、アセンブラなしでデスクトップの高レベル コードをより速く、より信頼性の高いものにするのに役立ちます。

于 2010-11-17T20:06:46.757 に答える
4

最近では、アセンブリ言語を使用する理由はほとんどありません。SSE や古い MMX のような低レベルの構造体でさえ、gcc と MSVC の両方に組み込み関数が組み込まれています (icc もそうだと思いますが、使用したことはありません)。

正直なところ、最近のオプティマイザーは非常に積極的で、ほとんどの人はアセンブリでコードを書くパフォーマンスの半分にも匹敵しません。メモリ内でのデータの順序付け方法を変更したり (ローカリティのために)、コンパイラにコードの詳細を伝えたり (を介して#pragma) することはできますが、実際にアセンブリ コードを作成すると...そこから余分なものが得られるとは思えません。

@VJo、高レベルの C コードで組み込み関数を使用すると、単一のアセンブリ命令を使用せずに同じ最適化を実行できることに注意してください。

また、次の Microsoft C++ コンパイラと、そこからインライン アセンブリを削除する方法についての議論がありました。それはその必要性について多くを語っています。

于 2010-11-17T08:45:39.533 に答える
3

場合によります。一部の状況では(まだ)行われていますが、ほとんどの場合、それだけの価値はありません。最新の CPU は非常に複雑であり、効率的なアセンブリ コードを記述することも同様に複雑です。そのため、ほとんどの場合、手動で記述したアセンブリは、コンパイラが生成できるアセンブリよりも遅くなります。

ここ 2 年以内に適切なコンパイラがリリースされたと仮定すると、通常は C/C++ コードを微調整して、アセンブリを使用する場合と同じパフォーマンス上の利点を得ることができます。

ここのコメントと回答の多くの人々は、アセンブリで何かを書き直して得た「N 倍のスピードアップ」について話していますが、それ自体はあまり意味がありません。C で流体力学方程式評価する C 関数を書き直して、アセンブリで記述する場合と同じ最適化の多くを適用し、ハードウェアを理解し、プロファイリングすることで、13 倍のスピードアップを得ました。最終的には、アセンブリで書き直す意味がないほど、CPU の理論上のピーク パフォーマンスに近づきました。通常、制限要因となるのは言語ではなく、作成した実際のコードです。コンパイラが困難な「特別な」命令を使用していない限り、

組み立ては魔法のように速くはありません。コンパイラをループから外すだけです。コンパイラは手動で行うのが本当に面倒な多くの最適化を実行するため、自分が何をしているのかを本当に理解していない限り、これはしばしば悪いことです。しかし、まれに、コンパイラがコードを理解できず、効率的なアセンブリを生成できないことがあります、アセンブリを自分で作成すると便利な場合があります。ドライバー開発など (ハードウェアを直接操作する必要がある場合) 以外で、アセンブリを作成する価値があると私が考えることができる唯一の場所は、効率的な SSE コードを生成できないコンパイラーに悩まされている場合です。組み込み関数 (MSVC など)。それでも、私は C++ の組み込み関数を使い始め、それをプロファイリングして、可能な限り微調整しようとしましたが、コンパイラはこれがあまり得意ではないため、最終的にそのコードを書き直す価値があるかもしれません。組み立て中。

于 2010-11-17T12:21:39.860 に答える
2

アセンブリ コードを使用してパフォーマンスを 6 倍向上させた、こちらをご覧ください。したがって、答えは次のとおりです。それはまだ行われていますが、コンパイラはかなり良い仕事をしています。

于 2010-11-17T08:38:49.467 に答える
1

私が行ったアセンブリ最適化の例がありますが、これも組み込みターゲットにあります。PC 用のアセンブリ プログラミングの例もいくつか見ることができます。これは非常に小さくて高速なプログラムを作成しますが、通常は努力する価値はありません (「windows 用のアセンブリ」を探してください。非常に小さくてきれいなプログラムがいくつか見つかります)。

私の例は、プリンター コントローラーを作成していたときで、50 マイクロ秒ごとに呼び出されるはずの関数がありました。多かれ少なかれ、ビットの再シャッフルを行う必要があります。C を使用すると約 35 マイクロ秒で実行でき、アセンブリを使用すると約 8 マイクロ秒で実行できました。これは非常に具体的な手順ですが、現実的で必要なものです。

于 2010-11-17T08:47:16.453 に答える
1

一部の組み込みデバイス (電話や PDA) では、コンパイラがそれほど成熟しておらず、非常に遅く、さらには正しくないコードを生成する可能性があるため、これは便利です。私は個人的に、ARM ベースの組み込みプラットフォーム用のいくつかの異なるコンパイラのバグのある出力を回避するか、アセンブリ コードを記述して修正する必要がありました。

于 2010-11-17T16:50:07.520 に答える
1

私の仕事では、低レベルのアクセスに組み込みターゲット (マイクロ コントローラー) のアセンブリを使用しました。

しかし、PCソフトとしては、あまり役に立たないと思います。

于 2010-11-17T08:38:33.203 に答える
0
  1. はい。インライン アセンブリまたはリンク アセンブリ オブジェクト モジュールを使用します。どの方法を使用する必要があるかは、記述する必要があるアセンブリ コードの量によって異なります。通常は、インライン アセンブリを数行使用し、複数の関数の場合はオブジェクト モジュールを一度に切り替えるだけで問題ありません。
  2. 確かに、しかし時にはそれが必要です。ここでの顕著な例は、オペレーティング システムのプログラミングです。
  3. 今日のほとんどのコンパイラは、高水準言語で記述したコードを、アセンブリ コードを記述するよりもはるかに最適化しています。人々はたいてい C のような高水準言語で書くのが不可能なコードを書くためにそれを使用します。誰かがそれを他の何かに使用する場合、その人は最新のコンパイラよりも最適化に優れている (私はそれを疑っています) か、単に愚かであることを意味します。たとえば、彼はどのコンパイラ フラグや関数属性を使用すればよいかわかりません。
于 2010-11-17T09:03:57.493 に答える