日本語版
最新ニュース
科学&テクノロジー

2025 年の Rust における SIMD の状況 |セルゲイ・”シュナツェル”・ダビドフ他 | 2011/11/1 2025 年 11 月

SIMDって何? SIMD を選択する理由演算を行うハードウェアは安価なので、今世紀に製造された CPU には十分な量が搭載されています。しかし、まだ命令デコード ブロックが 1 つしかなく、それを高速に実行するのが難しいため、演算ハードウェアはほとんど活用されていません。命令デコードのボトルネックを回避するには、加算などの 1 つの算術演算に対して、数値のバッチを一度に CPU に供給します。したがって、「単一命令、複数データ」、または略して SIMD という名前が付けられました。2 つの数値を加算する代わりに、数値の 2 つのバッチまたは「ベクトル」を加算することができ、所要時間はほぼ同じです。最近の x86 チップでは、これらのバッチのサイズは最大 512 ビットになるため、理論上は計算を 8 倍高速化できます。 u64 または 64 倍のスピードアップ u8!命令セット歴史的に、SIMD 命令は CPU アーキテクチャがすでに設計された後に追加されていたため、SIMD は各アーキテクチャで独自のマーケティング名を持つ拡張機能です。ARM はこれを「NEON」と呼び、すべての 64 ビット ARM CPU にこれが搭載されています。WebAssembly…

2025 年の Rust における SIMD の状況 |セルゲイ・”シュナツェル”・ダビドフ他 | 2011/11/1 2025 年 11 月

1762371652
2025-11-05 18:45:00

SIMDって何? SIMD を選択する理由

演算を行うハードウェアは安価なので、今世紀に製造された CPU には十分な量が搭載されています。しかし、まだ命令デコード ブロックが 1 つしかなく、それを高速に実行するのが難しいため、演算ハードウェアはほとんど活用されていません。

命令デコードのボトルネックを回避するには、加算などの 1 つの算術演算に対して、数値のバッチを一度に CPU に供給します。したがって、「単一命令、複数データ」、または略して SIMD という名前が付けられました。

2 つの数値を加算する代わりに、数値の 2 つのバッチまたは「ベクトル」を加算することができ、所要時間はほぼ同じです。

最近の x86 チップでは、これらのバッチのサイズは最大 512 ビットになるため、理論上は計算を 8 倍高速化できます。 u64 または 64 倍のスピードアップ u8

命令セット

歴史的に、SIMD 命令は CPU アーキテクチャがすでに設計された後に追加されていたため、SIMD は各アーキテクチャで独自のマーケティング名を持つ拡張機能です。

ARM はこれを「NEON」と呼び、すべての 64 ビット ARM CPU にこれが搭載されています。

WebAssembly にはマーケティング部門がないため、自社の製品を単に「WebAssembly 128 ビット パック SIMD 拡張機能」と呼んでいます。

64 ビット x86 には、128 ビット ベクトルの基本命令を備えた「SSE2」と呼ばれるものが付属していますが、 後で それに加えて、SSE 4.2 ではさらに多くの演算が追加され、AVX と AVX2 では 256 ビット ベクトルが追加され、AVX-512 では 512 ビット ベクトルが追加されるなど、さまざまな拡張機能が追加されました。

上の段落の「後で」という言葉は問題を引き起こします。

このCPUにはその命令があるのでしょうか?

x86 CPU でプログラムを実行している場合、CPU に特定の SIMD 拡張機能があるとは限りません。そのため、すべての x86 CPU で動作しないため、デフォルトではコンパイラーは SSE2 以降の命令を使用できません。

この問題を回避するには 2 つの方法があります。

自社のサーバーまたはパブリック クラウド上でのみバイナリを実行している企業に勤めている場合は、それらの企業はすべて、少なくとも 10 年以上前に導入された AVX2 を搭載するのに十分な最新のものであり、AVX2 なしで実行された場合にプログラムがクラッシュしたり誤動作したりする可能性があると断言できます。

RUSTFLAGS='-C target-cpu=x86–64-v3' cargo build --release

ただし、他の人が実行できるようにバイナリを配布している場合、それは実際には選択肢ではありません。

代わりに、次のようなことを行うことができます 関数のマルチバージョン化: 異なる SIMD 拡張子に対して同じ関数を複数回コンパイルし、プログラムが実際に実行されるときに、CPU がサポートする機能を確認し、それに基づいて適切なバージョンを選択します。

幸いなことに、この問題は x86 でのみ存在します。

ARM は、すべての 64 ビット CPU で NEON を必須にし、その後は 128 ビットを超えて幅を拡張することを気にしませんでした。 (技術的には 全て 存在しますが、2025 年の時点でもまだほとんどが紙の上にあり、それに対する Rust のサポートは終了しています。 まだ進行中)。

WebAssembly を使用すると、SIMD ありと SIMD なしの 2 つの異なるバイナリをコンパイルし、JavaScript を使用してブラウザが SIMD をサポートしているかどうかを確認できます。

ソリューションスペース

Rust の SIMD には、労力の少ない順に 4 つのアプローチがあります。

  1. 自動ベクトル化
  2. 派手な反復子
  3. ポータブル SIMD 抽象化
  4. 生の組み込み関数

自動ベクトル化

SIMD への最も簡単なアプローチは、コンパイラーに SIMD を実行させることです。

ベクトル化に適した方法でコードを構造化している限り、これは驚くほどうまく機能します。この記事ではそれについて説明します。

動作しているかどうかを確認できます カーゴショーASM または ゴッドボルト.orgしかし、結果を最終的に判断するのはベンチマークです。

残念ながら、コンパイラがベクトル化するコードの複雑さには制限があり、コンパイラのバージョンによって異なる可能性があります。今日何かがベクトル化されたとしても、それが 1 年後もベクトル化されるとは限りません。

このメソッドのもう 1 つの欠点は、オプティマイザが浮動小数点数に関係するものにさえ触れないことです (f32 そして f64 種類)。プログラムの監視可能な出力を変更することは許可されておらず、float 演算の順序を変更すると、精度の低下により結果が変わる可能性があります。 (精度の低下を心配しないようにコンパイラーに指示する方法はありますが、現時点では 夜限定)。

それで今、 浮動小数点数を処理する必要がある場合、自動ベクトル化は使用できません Rust コンパイラの夜間ビルドを使用できない場合は除きます。

(SIMD がなくても浮動小数点は呪われます。使用可能な方法でそれらの配列を合計するという単純なことが判明します。 本当に難しい)。

マルチバージョン機能への組み込みの方法はありませんが、 マルチバージョン クレートは自動ベクトル化とうまく連携します。

派手な反復子

まるで レーヨン スワップすることで反復子を並列実行できます .iter().par_iter()、SIMDについても同じことをしようとする試みがありました。結局のところ、SIMD とは別の種類の並列処理なのでしょうか?

これは、 もっと早く クレートがかかります。その箱は何年も放置されており、このアプローチがうまくいったようには見えません。

ポータブル SIMD 抽象化

そのアイデアは、次のようなデータのチャンクを明示的に操作することでアルゴリズムを記述できるようにすることです。 [f32; 8] ただし、カスタム型でラップし、次のような操作のカスタム実装を提供します。 + SIMD 命令にコンパイルされます。

std::simd まさにそれです。 LLVM がサポートするすべての命令セットをサポートしているため、プラットフォームのサポートは比類のありません。とよく合います マルチバージョン 木箱。残念ながらそれは 夜限定 当面はこのままになるため、ほとんどの状況では使用できません。

広い crate は成熟した確立されたオプションです。 NEON、WASM、およびすべての x86 命令セットをサポートします。しかし、それは マルチバージョン管理をサポートしていません まったく、次のような非常にエキゾチックで限定的なアプローチを除けば、 カーゴマルチバー

パルプ crate はマルチバージョン管理が組み込まれた優れた設計で、非常に完成度が高く完成しています。それは力を与えます ヤシの木、その性能は明確に証明されています。欠点は、WASM をサポートしていないことと、x86 では AVX2 と AVX-512 のターゲットのみをサポートし、古い拡張機能はサポートしていないことです。しかし、AVX2 は 2012 年に導入され、 Steam ハードウェア調査 95% のシステムにはそれが備わっているため、それは大したことではないかもしれません。

マセレーター crate は、大幅に拡張された命令セットのサポートを備えたパルプのフォークです。すべての x86 拡張機能、WASM、NEON、さらに LoongArch SIMD 拡張機能もサポートしています。それを使用するのは次の人だけです バーンアンドアレイ、そしてそこにもオプションの依存関係があります。紙の上では素晴らしく聞こえますが、奇妙に曖昧であり、したがって証明されていません。おそらくパルプを使用してコードを作成し、それをマセレーターに置き換えて、すべてがまだ機能し、必要な速度で実行されるかどうかを確認するでしょう。

恐れ知らずのシムド クレートは主にパルプのデザインをコピーしたもので、 老人。パルプよりもはるかに成熟度が低いですが、活発に開発中です。この記事の執筆時点では、NEON、WASM、および SSE4.2 をサポートしていますが、新しい x86 拡張機能はサポートしていません。まだ未熟なようですが、注目していきたいところです。

シムディーズ は、AVX-512 を除くすべての命令セットをサポートし、マルチバージョン管理が組み込まれているかなり古いクレートです。私が立ち止まるのは、何年も前から存在しているにもかかわらず、まだほとんど使用されていないということです。 SIMD を必要とする他の人は、SIMD を使用せずに自分で SIMD を構築しました。そして、その README には次のように書かれています。

現在、i32、i64、f32、および f64 タイプについては十分に具体化されています。

ということは、他の種類はまだ揃っていないのでしょうか?

TL;DR: 使用 std::simd 夜でもよければ、 wide マルチバージョン管理が必要ない場合、またはそうでない場合 pulp または macerator

これを読んでいる時点で 2025 年ではない場合は、チェックしてください fearless_simd、 なぜなら std::simdまだ あなたの輝かしい未来には毎晩、そうでしょう?

生の組み込み関数

金属に本当に近づきたければ、いつでもあります。 生の組み込み関数、プロセッサの命令から 1 つのステップを削除しただけです。

生の組み込み関数を使用する際に立ちはだかる問題は、ターゲットとするプラットフォームおよび命令セットごとに組み込み関数を手動で作成する必要があることです。一方 std::simd または wide ロジックを一度作成すると、それを自動的にアセンブリにコンパイルできます。組み込み関数を使用すると、サポートする単一のプラットフォームと命令セット (SSE、AVX、NEON など) ごとに個別の実装を作成する必要があります。とてもたくさんのコードがあります!

それらすべてに次のような名前が付けられているという事実は、実際には役に立ちません。 _mm256_srli_epi32 そして、コードは最終的に、これらの難解な名前の関数を呼び出す長いリストになります。そして、読みやすさを助けるラッパーは、次のような独自の問題を引き起こします。 マルチバージョン化との衝突 または 安全でないコード または 難解なマクロ

独自のマルチバージョン管理も構築する必要があります。というより、各命令セットに対して手動で作成した専用の実装に手動でディスパッチする必要があります。 std::is_x86_feature_detected! マクロは特徴の検出を処理しますが、多少遅いです。場合によっては、利用可能な機能を一度だけ検出して結果をキャッシュすることが有益ですが、それも手動で実装する必要があります。

明るい面としては、今年は組み込み関数の作成が著しく少なくなりました。それらのほとんどはもうありません unsafe Rust 1.86 以降を呼び出すと、 安全な_unaligned_simd crate は残りの安全なラッパーを提供します。

したがって、少なくともこのアプローチはもう廃止されています unsafe 他のすべての問題に加えて!

どちらがあなたに適していますか?

仕事に適したツールは最終的にはユースケースによって異なります。

依存関係をなくし、事前の手間もほとんど省きたいですか?自動ベクトル化。既存の C コードを移植しますか、それとも非常に特殊なハードウェアをターゲットにしますか?組み込み。他に何か?ポータブル SIMD 抽象化。

ここまで読んだので、記事の上部にある表を理解し、決定に役立てることができます。

#年の #Rust #における #SIMD #の状況 #セルゲイシュナツェルダビドフ他 #年 #月

執筆者について: nipponese

Nipponese News編集部は、国内外のニュースを日本語で分かりやすくお届けします。