8

Rust で何百万回も実行される (ピクセル処理を考えてください) 整数関数を作成する場合、C/C++ と同様に、最高のパフォーマンスで操作を使用すると便利です。

リファレンス マニュアルでは動作の変更について説明していますが、どのメソッドが標準(注 1 を参照)の整数算術演算よりもパフォーマンスが高いかは必ずしも明確ではありません。wrapping_addCの追加と同等のものにコンパイルされると思います。

標準演算 (加算 / 減算 / 乗算 / モジュロ / 除算 / シフト / ビット操作など) のうち、デフォルトでは使用されない、より高性能な代替手段を持つ演算はどれですか?


ノート:

  1. 標準では、 シンボルを使用した整数演算、または...などを意味します。数式を記述するときに使用するもの-オーバーフローをラップまたは返すメソッドのいずれかを使用する特別な必要がない限り。a + bi / kc % e
  2. この質問に答えるには、調査が必要かもしれません。したがって、結果のアセンブリを見て、どの操作がチェックされていない/プリミティブ操作を使用しているかを確認することで、いくつかのチェックを行うことができてうれしいです。
  3. チェックされた操作とチェックされていない操作の速度の違いは重要ではない可能性があります。その場合、関数の「高速」バージョンを作成して、「安全な」バージョンと比較できるようにしたいと考えています。与えられた機能にとってそれが合理的な選択であるかどうかについての私自身の結論に。
  4. ピクセル処理について言及しましたが、SIMD が可能な解決策として登場しました。これは良い提案ですが。SIMD を使用して最適化できないケースがまだ残っているため、高速な整数演算の一般的なケースはまだ考慮すべき事項です。
4

3 に答える 3

7

標準演算 (加算 / 減算 / 乗算 / モジュロ / 除算 / シフト / ビット操作など) のうち、デフォルトでは使用されない、より高性能な代替手段を持つ演算はどれですか?

Rust はパフォーマンスのために設計されていることに注意してください。その結果、整数演算はDebugでチェックされますが、特にコンパイラに特に指示しない限り、Releaseでラップするように定義されます。

その結果、デフォルト オプションを使用したリリース モードでは、以下の間で厳密にパフォーマンスの違いはありません。

  • +wrapping_add
  • -wrapping_sub
  • *wrapping_mul
  • /wrapping_div
  • %wrapping_rem
  • <<wrapping_shl
  • >>wrapping_shr

したがって、符号なし整数の場合、パフォーマンスは C または C++ のパフォーマンスとまったく同じです。ただし、符号付き整数の場合、符号付き整数のアンダーフロー/オーバーフローは C および C++ では未定義の動作であるため、オプティマイザーは異なる結果を生成する可能性があります (gcc および Clang は、-fwrapv符号付き整数に対してもラッピングを義務付けるフラグを受け入れますが、デフォルトではありません)。

checked_*ただし、 、overflow_*およびメソッドを使用するsaturating_*と、一般的に遅くなると思います。


したがって、興味深い接線は、スイッチを切り替えてチェック演算を明示的に要求するとどうなるかを理解することです。

現在、Rust 実装1は、アンダーフロー/オーバーフロー チェックの正確な実装です。加算、減算、乗算などはそれぞれ個別にチェックされ、オプティマイザはこれらの分岐を融合するのが得意ではありません。

具体的には、正確な実装により、一時的なオーバーフローが排除されます。オーバーフローの可能性があるため、5 + x - 5として最適化することはできません。また、一般的に自動ベクトル化もできなくなります。x5 + x

オプティマイザーがオーバーフローがないことを証明できる場合 (通常はそうではありません) のみ、最適化により適した分岐のないパスを取り戻すことができます。

一般的なソフトウェアでは、算術命令は全体のコストに占める割合が小さいため、影響はほとんど目立たないことに注意してください。ただし、この比率が上昇すると、非常に顕著になる可能性があり、実際、Clang を使用した SPEC2006 ベンチマークの一部に表示されます。

このオーバーヘッドは、チェックをデフォルトでアクティブにするのには不適切であると見なされるのに十分でした。

1 これは LLVM 側の技術的な制限によるものです。Rust の実装は LLVM に委譲するだけです。


将来的には、チェックのファジー実装が利用可能になることが期待されています。ファジー実装の背後にある考え方は、すべての操作をチェックするのではなく、実行するだけで、アンダーフロー/オーバーフローの場合にフラグが設定されるか、値が汚染されるというものです。次に、結果を使用する前に、チェック (分岐) が実行されます。

Joe Duffy 氏によると、彼らは Midori でそのような実装を行っており、パフォーマンスへの影響はほとんど目立たなかったため、実現可能であると思われます。ただし、LLVM で同様のものを作成しようとする試みはまだ認識していません。

于 2016-12-12T14:42:00.117 に答える
3

ピクセル処理を考える

その場合、単一値の操作をまったく考えるべきではありません。代わりに SIMD 命令を使用したい場合。これらは現在、安定した Rust では利用できませんが、一部はフィーチャー ゲート機能を介してアクセスでき、すべてはアセンブリを介して利用できます。

clang の場合のように、LLVM がコードを SIMD に最適化することは可能ですか?

aochagavia が既に回答したように、はい、LLVM は特定の種類のコードを自動ベクトル化します。ただし、最高のパフォーマンスを要求する場合、通常、オプティマイザーの気まぐれに任せたくありません。私は、通常のありきたりのコードで自動ベクトル化を期待する傾向があり、次に重い数学カーネル用の直線的なコードを書き、次に SIMD コードを書き、正確性と速度のベンチマークをテストします。

于 2016-12-12T13:38:53.963 に答える