[解決済み] アセンブリがCより速いのはどんなとき?[クローズド]
質問
アセンブラを知る理由の1つは、アセンブラを使うことで、上位言語、特にC言語で書くよりもパフォーマンスが高いコードを書ける場合があるからだと言われています。 しかし、それが完全に間違いというわけではないが、アセンブラを使うことでパフォーマンスが向上するケースもあるという話もよく聞く。 実際に より高性能なコードを生成するために使用されることは極めて稀であり、また、アセンブリに関する専門的な知識と経験が必要です。
この質問では、アセンブラの命令がマシン固有で移植不可能であることや、アセンブラの他の側面については触れません。 もちろん、これ以外にもアセンブラを知っている理由はたくさんありますが、これは例やデータを求める具体的な質問であって、アセンブラと高級言語について延々と論じるためのものではありません。
どなたか 具体例 また、その主張をプロファイリングで裏付けることができますか? 私はこのようなケースが存在することに自信を持っていますが、このようなケースがどの程度難解であるかを正確に知りたいのです。
どのように解決するのか?
ここで実例を紹介します。古いコンパイラでの固定小数点乗算。
これらは浮動小数点のないデバイスで便利なだけでなく、32ビットの精度と予測可能なエラー(floatは23ビットしかなく、精度の低下を予測するのは難しい)を与えるので、精度に関しても輝いています。
アブソリュート
の代わりに、全範囲の精度を提供します。
相対
の精度(
float
).
最近のコンパイラはこの固定小数点の例をうまく最適化するので、まだコンパイラ固有のコードが必要な最新の例については
-
64ビット整数乗算の高次部分の取得
: を使用した移植版
uint64_t
32x32 => 64 ビット乗算の場合、64 ビット CPU では最適化に失敗するため、イントリンシックまたは__int128
を使用すると、64ビットシステム上で効率的なコードを作成できます。 - Windows 32ビット版における _umul128 : MSVC は 32 ビット整数を 64 にキャストして乗算するとき、いつも良い仕事をしないので、 intrinsics はとても役に立ちました。
C言語には、完全乗算演算子(Nビットの入力から2Nビットの結果)がないのです。 Cでこれを表現する通常の方法は、入力をより広い型にキャストして、コンパイラが入力の上位ビットが興味深いものではないと認識することを期待することです。
// on a 32-bit machine, int can hold 32-bit fixed-point integers.
int inline FixedPointMul (int a, int b)
{
long long a_long = a; // cast to 64 bit.
long long product = a_long * b; // perform multiplication
return (int) (product >> 16); // shift by the fixed point bias
}
このコードの問題は、C言語では直接表現できないことを行っていることです。2つの32ビットの数値を掛け合わせて64ビットの結果を得たいのですが、その中央の32ビットを返しています。しかし、C言語ではこの乗算は存在しない。できることは、整数を64ビットに昇格して、64*64 = 64の乗算をすることだ。
しかし、x86(およびARM、MIPS、その他)は、1つの命令で乗算を行うことができます。コンパイラの中には、この事実を無視して、ランタイムライブラリ関数を呼び出して乗算を行うコードを生成するものがありました。16進シフトもしばしばライブラリルーチンで行われます(x86もこのようなシフトが可能です)。
つまり、乗算のためだけに1つか2つのライブラリーを呼び出すことになるのです。これは深刻な結果を招きます。シフトが遅くなるだけでなく、関数呼び出しの間、レジスタを保持しなければならず、インライン化やコードアンロールの助けにもならない。
同じコードを(インライン)アセンブラで書き直せば、大幅なスピードアップが可能です。
おまけ:ASMを使うのは問題解決に最適な方法ではありません。例えば、VS.NET2008コンパイラーは、32*32=64ビットのmulを__emulとして、64ビットのshiftを__ll_rshiftとして公開しています。
このような場合、イントリンシックスを使用すると、Cコンパイラが内容を理解できるような形で関数を書き換えることができます。これにより、コードのインライン化、レジスタの割り当て、共通部分式の除去、定数伝搬もできるようになります。を得ることができます。 巨大 この方法で、手書きのアセンブラコードよりもパフォーマンスが向上します。
参考までに。VS.NETコンパイラの固定小数点mulの最終結果は以下の通りです。
int inline FixedPointMul (int a, int b)
{
return (int) __ll_rshift(__emul(a,b),16);
}
固定小数点の除算の性能差はさらに大きいです。私は、重い固定小数点の除算コードを、asmの数行を書くことによって、最大10倍まで改善しました。
Visual C++ 2013を使用すると、どちらの方法でも同じアセンブリコードが得られます。
2007年のgcc4.1もピュアC版をうまく最適化しています。 (Godbolt コンパイラエクスプローラには、それ以前のバージョンの gcc はインストールされていませんが、おそらく古いバージョンの GCC でも、イントリンシックなしでこれを行うことができるでしょう)。
x86(32ビット)およびARM用のソースとasmは以下を参照してください。 ゴッドボルトコンパイラーエクスプローラー . (残念ながら、単純な純粋なCバージョンから悪いコードを生成するほど古いコンパイラはありません)。
最近のCPUは、C言語にはない演算子を使うことができる
まったく
のように
popcnt
またはビットスキャンで最初か最後のセットビットを見つける
. (POSIXでは
ffs()
関数がありますが、そのセマンティクスは x86 の
bsf
/
bsr
. 参照
https://en.wikipedia.org/wiki/Find_first_set
).
コンパイラによっては、整数のセットビットの数を数えるループを認識してコンパイルすることがある。
popcnt
を使用する方がはるかに確実です。
__builtin_popcnt
で、あるいは SSE4.2 のハードウェアだけをターゲットにしているならば x86 で。
_mm_popcnt_u32
から
<immintrin.h>
.
またはC++で
std::bitset<32>
を使用し
.count()
. (これは、言語が、常に正しいものにコンパイルされ、ターゲットがサポートするものを利用できる方法で、標準ライブラリを通して最適化された popcount の実装を移植可能に公開する方法を発見した場合です)。 以下も参照してください。
https://en.wikipedia.org/wiki/Hamming_weight#Language_support
.
同様に
ntohl
にコンパイルすることができます。
bswap
(エンディアン変換のためのx86 32ビットバイトスワップ)を持ついくつかのCの実装で。
イントリンシックや手書きのASMのもう一つの主要な分野は、SIMD命令による手動ベクトル化です。 のような単純なループであれば、コンパイラは悪くありません。
dst[i] += src[i] * 10.0;
しかし、物事がより複雑になると、自動ベクトル化がうまくいかなかったり、まったく行われなかったりすることがよくあります。 たとえば、次のようなものはほとんどないでしょう。
SIMDを使ったatoiの実装方法は?
スカラーコードからコンパイラが自動的に生成する。
関連
-
initializer element is not constant "というエラーが表示されるのですが?
-
警告: 'struct XXX' はパラメータリストの内部で宣言されています。
-
[解決済み] B "の印刷が "#"の印刷より劇的に遅いのはなぜですか?
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] <は<=より速いのか?
-
[解決済み] \0-9]よりも効率が悪い
-
[解決済み] 難読化Cコードコンテスト2006。sykes2.cの解説をお願いします。
-
[解決済み] Collatz予想の検証を行うC++のコードは、なぜ手書きのアセンブリよりも高速に動作するのでしょうか?
-
[解決済み] なぜJavaでは2 * (i * i)の方が2 * i * iより速いのですか?
-
[解決済み] なぜ[]はlist()よりも速いのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
C 言語のポインタ配列のポインタ型、ポインタに値を割り当てるために配列名を使用、コンパイル時の警告:互換性のないポインタ型からの初期化
-
C - 添え字の値が配列でもポインタでもベクトルでもないエラー
-
警告:符号付き整数式と符号なし整数式の比較 [-Wsign-compare]
-
[解決済み] MIPSのネストされたForループと配列の使用
-
[解決済み] munmap_chunk(): 無効なポインタ
-
[解決済み] "static const" vs "#define" vs "enum"
-
[解決済み] C - Setデータ構造を実装するには?
-
[解決済み] Collatz予想の検証を行うC++のコードは、なぜ手書きのアセンブリよりも高速に動作するのでしょうか?
-
[解決済み] なぜC言語では構造体を頻繁にtypedefする必要があるのですか?
-
[解決済み] LD_PRELOADのトリックとは何ですか?