質問
私は、新しい言語を学ぶことで得られる最も重要なことの1つは、新しい言語の使い方ではなく、そこから得られる概念の知識であるという考えを固く信じています。 Assemblyがあなたにとっていかに重要または有用であると思うかを尋ねたり、実際のプロジェクトで絶対に使用しないかどうかは気にしません。
私が知りたいのは、一般的なプログラマーにとって最も重要だと思うアセンブリの概念です。アセンブリに直接関連している必要はありません-高級言語ですべての時間を費やしている典型的なプログラマーが理解できないか、またはCPUキャッシュなどの当たり前のことだと感じることもあります。
解決
アセンブリ言語は多くのささいなことといくつかの大きな概念を教えてくれると思います。
ここで考えられるいくつかのことをリストしますが、x86とRISC命令セットの両方を使用して学習し、使用することに代わるものはありません。
おそらく整数演算が最速だと思うでしょう。整数の整数平方根(つまりfloor(sqrt(i)))を見つけたい場合は、整数のみの近似ルーチンを使用するのが最善ですよね?
はい。 (つまりx86上の)数値演算コプロセッサーには、 fsqrt 命令があります。浮動小数点数への変換、平方根の取得、および再び整数への変換は、すべて整数のアルゴリズムよりも高速です。
次に、メモリにアクセスするなどの方法がありますが、アセンブリを掘り下げるまで、適切に評価することはできません。リンクリストがあり、リストの最初の要素に頻繁にアクセスする必要がある変数が含まれているとします。リストはめったに並べ替えられません。その変数にアクセスする必要があるたびに、リスト内の最初の要素へのポインターをロードし、それを使用して変数をロードする必要があります(使用中にレジスター内の変数のアドレスを保持できないと仮定) 。代わりにリスト外に変数を保存した場合、必要なロード操作は1つだけです。
もちろん、ここで数サイクルを節約しますが、最近は通常重要ではありません。しかし、高速である必要があるコードを書くことを計画している場合、この種の知識はインラインアセンブリと一般に他の言語の両方で適用できます。
呼び出し規約はどうですか? (一部のアセンブラーがこれを処理します-実際のプログラマーはこれらを使用しません。)呼び出し元または呼び出し先はスタックをクリーンアップしますか?スタックを使用しますか?レジスターに値を渡すことができます-しかし、面白いx86命令セットのため、特定のレジスターに特定のものを渡すことをお勧めします。そして、どのレジスタが保存されますか? Cコンパイラが単独で最適化できないことの1つは、呼び出しです。
リターンアドレスをプッシュしてからプロシージャにJMPするなど、ちょっとしたトリックがあります。プロシージャが戻ると、プッシュされたアドレスに移動します。関数呼び出しについての通常の考え方からのこの離脱は、「啓蒙の状態」のもう1つです。革新的な機能を備えたプログラミング言語を設計したことがある場合は、ハードウェアで可能な面白いことを知っておく必要があります。
アセンブリ言語の知識は、コンピュータセキュリティに関するアーキテクチャ固有のことを教えます。バッファオーバーフローを悪用する方法、またはカーネルモードに侵入する方法、およびそのような攻撃を防ぐ方法。
次に、自己修正コードの非常に優れた機能があり、関連する問題として、再配置やコードへのパッチの適用などのメカニズムがあります(これにはマシンコードの調査も必要です)。
しかし、これらすべてのことには正しい種類の心が必要です。あなたが置くことができるような人なら
while(x--)
{
...
}
それが何をするのかを一度学んでも、それを自分で解決するのは難しいと思うので、アセンブリ言語はおそらく時間の無駄です。
他のヒント
割り当てと管理の登録
Assemblyは、CPUが同時にジャグリングできる変数(マシンワードサイズの整数)の数に関する非常に良いアイデアを提供します。少数の一時変数のみを含むようにループを分解できる場合、それらはすべてレジスターに収まります。そうでない場合、物事がメモリにスワップアウトされると、ループの実行が遅くなります。
これは、Cコーディングの助けになりました。できるだけ少ないスパゲッティで、すべてのループをタイトでシンプルにしようとしています。
x86は愚かです
いくつかのアセンブリ言語を学ぶことで、x86命令セットがいかに不十分であるかを実感しました。可変長命令?タイミングを予測するのは難しいですか?非直交アドレス指定モード?うーん。
私たち全員がMIPS、またはARMやPowerPCを実行した場合、世界はより良くなるでしょう。 -これらのすべての償還品質を備えたx86プロセッサーの代わりに安価なMIPSプロセッサー。
アセンブリの言語を知っておくと、コンピュータがどのように機能するかを「フードの下で」理解できるようになります。そして、それはあなたが何かをデバッグしているときに役立ち、すべてのデバッガーはあなたに少なくとも問題が何であるかを理解するための戦いの機会を与えるアセンブリコードのリストを提供することができます。ただし、CPUが命令をキャッシュする方法を活用し、不安定な高レベルコードを記述してコンパイラに超効率的なマシンコードを生成させるなど、低レベルの知識を高レベルプログラミング言語に適用しようとすることは、おそらくマイクロ最適化を試みていることを示すサイン。ほとんどの場合、パフォーマンスの向上が必要な場合を除いて、通常はコンパイラーの裏をかこうとしない方が良いでしょう。パフォーマンスの向上が必要な場合は、とにかくアセンブリーでこれらのビットを記述することもできます。
それで、物事の仕組みをよりよく理解するためにアセンブリを知っておくのは良いことですが、得られた知識は必ずしも高水準言語でコードを書く方法に直接適用できるとは限りません。ただし、そのメモでは、アセンブリコードレベルで関数呼び出しがどのように機能するかを学習すること(スタックおよび関連レジスタについて学習する、スタックでパラメーターが渡される方法を学習する、自動ストレージがどのように動作するかを学習するなど)ができることがわかりました「スタック領域が足りない」など、高レベルのコードにあった問題を理解しやすくなりました。エラーと「無効な呼び出し規約」エラー。
最も重要な概念は、SIMDとその創造的な使用です。 SIMDを適切に使用すると、文字列処理からビデオ操作、行列演算まで、あらゆるものに及ぶ膨大な種類のアプリケーションで、パフォーマンスが大幅に向上します。これは、純粋なCコードの 10倍のパフォーマンスの向上を克服できる場所です。これが、単なるデバッグを超えてアセンブリが依然として有用な理由です。
私が取り組んでいるプロジェクトの例(すべての数値はCore 2のクロックサイクル数です):
逆8x8 H.264 DCT(周波数変換):
c: 1332
mmx: 187
sse2: 127
8x8クロマモーション補正(バイリニア補間フィルター):
c: 639
mmx: 144
sse2: 110
ssse3: 79
4 16x16絶対差演算の合計(モーション検索):
c: 3948
mmx: 278
sse2: 231
ssse3: 215
(はい、そうです-Cの18倍以上高速です!)
16x16ブロックの平均二乗誤差:
c: 1013
mmx: 193
sse2: 131
16x16ブロックの分散:
c: 783
mmx: 171
sse2: 106
メモリ、レジスタ、ジャンプ、ループ、シフト、およびアセンブラで実行できるさまざまな操作。私はアセンブリ言語クラスのプログラムをデバッグする日々を見逃すことはありません。 -しかし、それは確かに良い基盤を与えてくれました。
私たちは、今日使用している(そして私が愛している)このすべての派手なパンツのものが、最終的にこれらすべてのものに要約されることを忘れています(またはおそらく知らなかった)。
今、アセンブラーを知らなくても、確かに生産的で収益性の高いキャリアを持つことができますが、これらの概念は知っておくと良いと思います。
アセンブリの再帰とループを学ぶことで、多くのことが学べたと思います。使用している言語のコンパイラー/インタープリターがどのようにスタックにプッシュし、必要に応じてポップアウトするかという基本的な概念を理解しました。また、悪名高いスタックオーバーフローを悪用する方法も学びました。 (これは、Cでgetコマンドとputコマンドを使用すれば驚くほど簡単です。)
毎日の状況でasmを使用する以外は、アセンブリから学んだ概念を使用するとは思わない。
アドレス指定モードは非常に重要だと思います。
私の母校はそれを極端に取りましたが、x86には十分ではなかったため、PDP11のシミュレーターですべてを調べました。振り返ってみると、それは良い選択でした。
タイミング
高速実行:
- 並列処理
- 簡単な手順
- ルックアップテーブル
- 分岐予測、パイプライン処理
高速から低速のストレージへのアクセス:
- 登録
- キャッシュ、およびさまざまなレベルのキャッシュ
- メモリヒープとスタック
- 仮想メモリ
- 外部I / O
最近では、x86 asmはCPUの本質への直接的な行ではなく、APIの多くです。作成するアセンブラーのオペコード自体は、完全に異なる命令セットにコンパイルされ、再配置され、書き換えられ、修正され、一般に認識できないほどマングルされます。
学習アセンブラは、CPUの内部で何が起こっているかについての基本的な洞察を与えるものではありません。私見、アセンブラーを学ぶことよりも重要なのは、ターゲットCPUとメモリ階層がどのように機能するかをよく理解することです。
この一連の記事では、後者のトピックについてかなり詳しく説明しています。