vectorization について
.NET SIMD vectorizationは、SIMDで.NETコードを最適化する開発者向けのスキルです。スカラー動作を保つポータブルでメモリー安全な実装の指針に加え、テストとベンチマークの要件を示します。
主な機能
- 01既存の高速化フレームワークAPIを優先
- 02ポータブルなSIMDディスパッチ指針
- 03末尾処理とバッファー重複への安全な対応
- 04スカラーとベクトル経路のテスト
- 05スカラー実装とのベンチマーク比較
活用シーン
- 01フレームワークAPIを確認して数値ループを最適化
- 02SIMDの境界条件と重複処理を検証
- 03幅の広いベクトル経路の有効性を測定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill vectorizationインストール後、Claude Code で/vectorizationを入力して読み込まれたか確認します。
詳細
.NET SIMD ベクトル化
スカラーの契約を維持し、あらゆる長さでメモリ安全性を保ち、測定結果によって複雑さに見合う効果を示す、移植性の高い最適化を実装します。まず、公式の SIMD とハードウェア組み込み関数のガイダンスを読み、 包括的な実装テンプレートに従ってください。特に、幅ごとの自己完結したディスパッチ、専用の小規模入力処理、ループ、余りの処理からなる形を、幅チェックの連鎖に単純化せずに使用してください。このスキルでは、実際のコードを変更する際に適用する判断基準と検証項目を示します。
入力と前提条件
ユーザーに尋ねる前に、リポジトリから以下を確認してください。
| 入力 | 必須 | 確認する内容 |
|---|---|---|
| スカラー実装とテスト | はい | 既存の契約、代表的な呼び出し箇所、サポートされる重なり |
| ターゲットフレームワークとプラットフォーム | はい | 利用可能な SIMD API と、一貫した動作が必要なアーキテクチャ |
| ビルドとテストのワークフロー | はい | リポジトリの標準コマンドと、別々のテストプロセスの起動方法 |
| 代表的なワークロードまたはベンチマーク | 最適化の場合 | 一般的な入力サイズと、上回るべきベースライン |
API が存在するというだけでパッケージを追加しないでください。まずターゲットフレームワークと、プロジェクト既存の依存関係およびバージョン管理の方針を確認してください。
基本ルール
- 契約に合う最も高水準の API を使い、それ以上は進みません。
Span<T>とstringの操作、TensorPrimitives、テンソル型は、すでに多くの操作を高速化しています。ソースが基になるスパンを公開している場合、Sum、Min、Max、Averageなどの LINQ 集約も高速化できることがあります。空入力時の動作と浮動小数点の挙動を確認し、名前が似ている操作を同等だと決めつけないでください。既存の API で契約を維持できるなら、それを使い、手書きの SIMD には進まないでください。明示的なループを書く前に、検討したフレームワーク API と、どれも適用できない理由を明示してください。グラフィックスなどの領域では、固定形状のSystem.Numerics型が引き続き適切です。 - 新しい明示的な SIMD ループは
Vector128<T>から始めます。 最も幅広いハードウェアで高速化されます。測定結果で正当化される場合に限り、より幅の広い固定幅パスを追加してください。 - プラットフォーム間で一貫性を保ちます。 固定幅ベクトル型のクロスプラットフォーム操作を優先してください。これらは適切な対象命令に変換されます。たとえば、
(vector & mask) == Vector128<byte>.Zeroは x86/x64 ではptestになります。アーキテクチャ固有の組み込み関数は、測定で性能差が確認された場合にのみ使用し、IsSupportedでガードして、同等の移植可能な動作またはスカラー動作を維持してください。 IsHardwareAccelerated、IsSupported、Countを直接読み取ります。 JIT はこれらを定数として扱うため、キャッシュしても利点はなく、どの分岐が取り除かれるかも分かりにくくなります。- 明確な場合は演算子を優先します。 ビット演算子と比較演算子を組み合わせる式は、優先順位が明確になるように括弧で囲んでください。
タスクがレビューのみの場合、コードを書き換えないでください。性能上の改善案より先に、正しさとメモリ安全性の欠陥を報告してください。
作成チェックリスト
- コントラクト: 実装を変更する前に、空および短い入力、オーバーラップ、オーバーフロー、NaN、符号付きゼロ、順序、例外に対する動作を確認します。
- フレームワークの確認: 対象のフレームワークと既存のパッケージ参照を調べ、必要なエッジケースを使って、最上位候補の API をコンパイルするか動作を確認します。空入力時の特殊な動作を維持するなどの小さなコントラクトアダプターは、操作を再実装する理由にはなりません。API がコントラクトを満たす場合はそれを使い、そこで終わりにします。確認せずに利用できないと主張してはいけません。
- 構成: 新しい明示的な SIMD では、まず
Vector128<T>とスカラーを実装します。より幅の広いパスが測定結果によって正当化される場合に限り、Vector512<T>、次にVector256<T>、必要に応じてVector<T>、Vector128<T>、最後にスカラーの順に確認します。実装で不要なパスは省略します。固定幅ごとの外側のガードでは、そのIsHardwareAcceleratedプロパティと、ジェネリックな要素型の場合はIsSupportedだけを確認します。そのブロック内では、入力に少なくともCount個の要素があれば幅に対応したヘルパーを実行し、そうでなければ専用の小入力用ヘルパーを実行してから戻ります。長さの確認を外側のガードに置いて、より狭い幅へのディスパッチを繰り返すフォールスルーを起こしてはいけません。サポートされる幅ごとのブロックを自己完結させておくと、JIT がサポートされていないブロックを削除でき、一般的な小入力での冗長な処理を避けられます。 - 読み込みと書き込み: スパンベースの
Vector128.Create(span)とCopyToを優先します。JIT によって効率的に処理され、ピン留めや参照演算も必要ありません。安全でない読み込みと書き込みは、ほとんどの場合不要です。パスでマネージド参照を使ってバッファーを走査する必要が本当にある場合は、ポインターや手動で進めた参照ではなく、要素オフセットを取るLoadUnsafe(ref T, nuint)とStoreUnsafeのオーバーロードを使います。 - 空入力: 参照ベースのパスでは、要素
0をインデックス指定して取得するのではなく、MemoryMarshal.GetReference(span)またはMemoryMarshal.GetArrayDataReference(array)で開始参照を取得します。 - サポートされない要素型: 固定幅ベクトルがサポートするのはプリミティブな数値型であり、
charやboolではありません。MemoryMarshal.CastまたはAs<TFrom, TTo>で再解釈します。再解釈によって変わるのは型だけで、ビットは変わりません。ブール値のデータは0または1に、文字は有効な UTF-16 に保ち、必要に応じて格納前に結果を正規化します。 - オフセット:
Countを減算したり、インデックスをnuintに変換したりする前に、入力にベクトル全体が含まれていることを証明します。そうしないと、負の値が巨大な符号なしオフセットになります。 - マネージド参照: スパンの先頭より前や末尾を越えた位置への参照を作成してはいけません。末尾のちょうど 1 つ先への参照も同様です。ランタイムでは、オブジェクトまたは配列の 1 つ先を指す参照を逆参照せずに保持することが許可されていますが、このガイダンスでは脆弱で誤用しやすいため、そのパターンを意図的に禁止しています。ベース参照を有効な範囲内に保ち、要素オフセットで走査を表現します。
- 余り:
0、Count - 1、Count、Count + 1、各幅の倍数でない長さを含め、すべての長さを処理します。入力にベクトル全体が含まれる場合は、最後の完全なベクトルを再処理して、末尾もベクトル化したままにします。冪等な操作では、この重複部分をそのまま畳み込めます。冪等でない操作では、畳み込む前にConditionalSelectを使って、繰り返されるレーンを操作の単位元に置き換える必要があります。これは JIT が認識する一般的なパターンです。より幅広い最適化の機会を維持しながら、ゼロを単位元とする選択をビット単位のマスクに簡略化できる場合があります。インプレース変換では、オーバーラップする書き込みの前に元の末尾の値を保持し、有効な結果だけを書き込みます。 - バッファーのオーバーラップ: まだ読み込んでいない値が書き込みによって破損しないよう、走査方向またはステージング戦略を選びます。
- 数値の動作: アルゴリズムが依存する場合は、浮動小数点演算の結合の変更、NaN と符号付きゼロのセマンティクス、整数の checked または unchecked オーバーフロー、エンディアンを考慮します。
NativeとEstimateの操作は、精度や IEEE のエッジケースに関する動作を意図的に緩和できます。コントラクトで許可され、測定結果によって正当化される場合に限り使用します。 公式のガイダンスには、ディスパッチ、小さな入力、アンローリング、残りの要素を処理する完全なテンプレートが含まれているため、完全な実装にはそれらを使用してください。以下の抜粋は、自己完結したディスパッチブロックによって少なくとも1つの完全なベクトルがあることを確認した後、インプレースの要素ごとの変換を行う、安全なVector128<T>内側ループのみを示しています。Transformは実装対象の処理を表します。
Span<int> tail = data.Slice(data.Length - Vector128<int>.Count);
Vector128<int> end = Vector128.Create<int>(tail);
Span<int> remaining = data;
while (remaining.Length >= Vector128<int>.Count)
{
Vector128<int> values = Vector128.Create<int>(remaining);
Transform(values).CopyTo(remaining);
remaining = remaining.Slice(Vector128<int>.Count);
}
if (!remaining.IsEmpty)
{
Transform(end).CopyTo(tail);
}
先行する end の読み込みにより、重複する書き込みの前に元の値が保持されます。読み取り専用のリダクションでは、メインループの後に同じ最終スパンを読み込み、ConditionalSelect を使って処理済みのレーンを演算の単位元に置き換えてください。スパンの境界チェックを避けるためだけに、LoadUnsafe/StoreUnsafe やスカラーのエピローグに置き換えないでください。
テストチェックリスト
- 境界長、ランダムな値、空の入力、サポート対象の重複、数値のエッジケースについて、最適化した実装とスカラーの仕様を比較してください。実装したすべての幅とスカラーパスを、十分に大きく効果が見込める入力と、小さすぎて効果が見込めない入力の両方でテストしてください。
- 実装したすべての幅とスカラーフォールバックを、個別のプロセスで実行してください。x86/x64 の CoreCLR では、
DOTNET_EnableAVX2=0で AVX2 が無効になり、DOTNET_EnableHWIntrinsic=0でハードウェアイントリンシックが無効になります。リポジトリ標準のテストコマンドを使用し、単体テスト内でこれらのプロセス全体に適用される設定を変更しないでください。これらの設定は、ReadyToRun または事前コンパイル済みのコードを変更しないため、パスを強制する際は対象コードが JIT コンパイルされることを確認してください。 - 安全でない読み込みと書き込みについては、利用可能な場合、ガードページまたは同等の境界テストを使用してください。前方向の反復ではアクセス不可のページをバッファーの後ろに、後方向の反復では前に配置し、要素数がベクトル幅の倍数でないケースも含めてください。通常の配列割り当てでは、範囲外読み取りが確実に検出されるとは限りません。
ベンチマーク
追加した複雑さを維持する前に、BenchmarkDotNet を使用して、代表的な小規模および大規模の入力を測定してください。同じ実行内で、スカラー、Vector128<T>、実装した各ワイドパスを比較してください。セットアップ処理が支配的になるため、小さな入力では遅くなる場合があります。また、メモリースループット、アラインメント、レイテンシーの影響があるため、理論上のベクトル幅の倍率どおりに高速化することはほとんどありません。ばらつきの状況を添えてスループットまたは処理時間を報告し、該当する場合は生成コードのサイズや命令数も報告してください。安定した測定には割り当てのアラインメントを制御するか、分布を観察するためにランダム化してください。幅の広いベクトルが常に高速とは限りません。
必要なフレームワークを対象にできない場合、該当するアーキテクチャーで実行できない場合、またはフォールバック構成を実行できない場合は、未検証のパスを正確に明記してください。デフォルトのハードウェアでのテストだけを根拠に、成功したと主張しないでください。
完了条件
- 作成: スカラーの仕様をテストでカバーし、選択したフレームワークまたは SIMD レイヤーを特定してください。代表的なワークロードの測定結果を報告し、実行できなかったアーキテクチャーまたはフォールバックパスがあれば明記してください。
- レビュー: 具体的な指摘のみを、正確性、メモリー安全性、移植性、テスト、パフォーマンスの根拠の順に報告してください。指摘が残っていない場合は、その旨を明確に述べてください。
- ビルドできるだけの場合、現在のマシンでのみテストに合格する場合、またはスカラーのベースラインと比較していない場合は、最適化が完了したとみなさないでください。
レビューチェックリスト
次の順にレビューしてください。
- 符号付きゼロ、NaN、オーバーフロー、関連するエンディアンを含む、スカラーの仕様との同等性
- 既存の高速化されたフレームワーク API の再利用
- 冪等な処理と非冪等な処理それぞれにおけるテールの正確性
- メモリー安全性、符号なしオフセット演算、空の入力、バッファーの重複
- 移植可能なディスパッチと、動作上同等なフォールバック
- 各幅とスカラーパスを強制するテスト
- 明示的な SIMD と追加の幅を正当化するベンチマーク