プロセッサー固有の SIMD 拡張命令
Burst では、Unity.Burst.Intrinsics.X86
ファミリのネストされたクラスで、SSE から AVX2 までのすべての Intel SIMD intrinsic を公開しています。Unity.Burst.Intrinsics.Arm.Neon
クラスでは、Arm Neon の Armv7、Armv8、Armv8.2 (RDMA、crypto、dotprod) の intrinsic を提供しています。
コードの整理
以下の intrinsic はプレーンな静的関数を含んでいるため、静的にインポートする必要があります。
using static Unity.Burst.Intrinsics.X86;
using static Unity.Burst.Intrinsics.X86.Sse;
using static Unity.Burst.Intrinsics.X86.Sse2;
using static Unity.Burst.Intrinsics.X86.Sse3;
using static Unity.Burst.Intrinsics.X86.Ssse3;
using static Unity.Burst.Intrinsics.X86.Sse4_1;
using static Unity.Burst.Intrinsics.X86.Sse4_2;
using static Unity.Burst.Intrinsics.X86.Popcnt;
using static Unity.Burst.Intrinsics.X86.Avx;
using static Unity.Burst.Intrinsics.X86.Avx2;
using static Unity.Burst.Intrinsics.X86.Fma;
using static Unity.Burst.Intrinsics.X86.F16C;
using static Unity.Burst.Intrinsics.X86.Bmi1;
using static Unity.Burst.Intrinsics.X86.Bmi2;
using static Unity.Burst.Intrinsics.Arm.Neon;
Burst の CPU intrinsic は、特定の CPU 命令に変換されます。しかし、Burst には、Burst AOT Settings
に設定された CPU ターゲットとコードで使用されている intrinsic との間に互換性があることを確認する特別なコンパイラーパスがあります。そのため、サポートされていない命令 (例: Intel CPU の AArch64 Neon、SSE4 CPU の AVX2 命令) の呼び出し試行は行われず、"Invalid instruction (無効な命令)" 例外でプロセスが中止になることはありません。この確認に失敗した場合、コンパイラーエラーが生成されます。
ただし、CPU ターゲットごとにコードパスを複数指定するか、intrinsic コードにターゲット CPU との互換性があることを確認することが目的の場合には、intrinsic コードを以下のプロパティチェックでラップします。
- IsNeonSupported
- IsNeonArmv82FeaturesSupported
- IsNeonCryptoSupported
- IsNeonDotProdSupported
- IsNeonRDMASupported
以下に例を示します。
if (IsAvx2Supported)
{
// AVX2 命令のコードパス
}
else if (IsSse42Supported)
{
// SSE4.2 命令のコードパス
}
else if (IsNeonArmv82FeaturesSupported)
{
// Code path for Armv8.2 Neon instructions
}
else if (IsNeonSupported)
{
// Arm Neon 命令のコードパス
}
else
{
// それ以外のフォールバックパス
}
上記の分岐はパフォーマンスに影響しません。Burst は、IsXXXSupported
プロパティをコンパイル時に評価し、サポートされていない分岐をデッドコードとして除外します。アクティブな分岐は if チェックなしでそのまま残ります。新しい機能レベルにはそれ以前の機能レベルが暗示的に含まれているため、テストは最新のものから古いものの順に整理するようにしてください。現在のコンパイルターゲットに含まれていない intrinsic を使用すると、Burst でコンパイル時エラーが発生します。これらは Burst では機能レベルテストでひとまとめにされないので、機能テストに含める内容を絞り込むのに役立ちます。
Burst を有効にせずに .NET、Mono、または IL2CPP で作成したアプリケーションを実行する場合、すべての IsXXXSupported
プロパティで false
が返されます。しかし、テストをスキップしても、Mono では (以下に示した例外を除く) ほとんどの intrinsic の参照バージョンを変わらず実行できます。そのため、マネージデバッガーを使用する必要がある場合には役立ちます。参照実装は実行速度が遅く、マネージデバッグ専用です。
Important
Arm Neon intrinsic には、参照マネージ実装はありません。そのため、Mono で intrinsic をステップ実行するという、前の段落で説明した手法は使用できません。また、double 型の演算を行う FMA intrinsic にはソフトウェアフォールバックがありません。これは、融合 64 ビット浮動小数点演算をエミュレートする際の継承が複雑であるためです。
Intrinsic で使用する型は、v64
(Arm のみ)、v128
、v256
です。これらはそれぞれ、64 ビット、128 ビット、256 ビットのベクトルを表しています。例えば、NativeArray<float>
と v128 シャッフルマスクのルックアップテーブル Lut
がある場合に、ベクトルのロード/保存の再解釈と直接の intrinsic 呼び出しを使用して left-lane パッキングを実行する方法を以下のコードフラグメントに示します。
v128 a = Input.ReinterpretLoad<v128>(i);
v128 mask = cmplt_ps(a, Limit);
int m = movemask_ps(a);
v128 packed = shuffle_epi8(a, Lut[m]);
Output.ReinterpretStore(outputIndex, packed);
outputIndex += popcnt_u32((uint)m);
Intel intrinsics
Intel intrinsics API は C/C++ Intel instrinsics API によく似ていますが、以下の点が異なります。
- 128 ビットのベクトル型 (
**m128
、**m128i
、__m128d
) はすべてv128
にまとめられています。 - 256 ビットのベクトル型 (
**m256
、**m256i
、__m256d
) はすべてv256
にまとめられています。 - C# には名前空間があるので、命令とマクロの
_mm
プレフィックスはすべて削除されています。 - ビットフィールド定数 (例: 丸めモードの選択) はすべて、C# のビットフラグ enum 値に置き換えられています。
Arm Neon intrinsics
Arm Neon intrinsics API は Arm C 言語拡張 によく似ていますが、以下の点が異なります。
- ベクトル型はすべて
v64
とv128
にまとめられており、型指定がなくなっています。そのため、API の呼び出し時には、ベクトル型に目的の要素の型と数を含める必要があります。 - ベクトル型
*x2
,*x3
,*x4
はサポートされていません。 poly*
型はサポートされていません。reinterpret*
関数はサポートされていません (ベクトル型v64
とv128
を使用するので、不要)。- intrinsic の使用は、Armv8 (64 ビット) ハードウェアでのみサポートされます。
Burst の CPU intrinsic では、型指定なしのベクトルを使用します。そのため、Burst では型の確認を一切行いません。例えば、ベクトルに含まれる 4 つの int を処理する intrinsic を呼び出したときに、そのベクトルの要素が 4 つの float に初期化されていても、コンパイラーエラーは発生しません。ベクトル型のフィールドは、共用体のような構造体と同様に、すべての要素型を表します。そのため、コードに最も適した方法で柔軟に intrinsic を使用することができます。
Arm Neon C intrinsics (ACLE) は、int32x4_t などの型指定されたベクターを使用するとともに、別の要素型のベクトルに変換するための特別な API (reinterpret_\*
など) を備えています。Burst の CPU intrinsic のベクトルは型指定が無いので、これらの API は不要です。同等の機能は、以下の API で提供されます。
Burst でサポートされている Arm Neon intrinsic のカテゴリ別インデックスについては、Arm Neon intrinsic リファレンス を参照してください。