🏹
Dart で SIMD を使う
概要
dart:typed_data
の Float32List
などのデータを扱うとき, CoreML などのネイティブの機能を使った実装までするのは面倒だけど, もうちょっとパフォーマンス改善したい. そんな場面で SIMD を使うと Dart で書ける範囲でパフォーマンス改善できます.
関連するクラス
float32, float64, int32 に対応するクラスがあります.
例
ここでは, サイズが同じ二つの Float32List
の各成分に同じ二項演算(和など)を施す例を示します.
可能な限り Float32x4
で処理して, 最後の余りを double
で処理しています.
Float32List simdBinaryOperation(
Float32List list1,
Float32List list2, {
required Float32x4 Function(Float32x4, Float32x4) simdOperation,
required double Function(double, double) remainingOperation,
}) {
if (list1.length != list2.length) {
throw ArgumentError(
'Lists must have the same length: '
'${list1.length} != ${list2.length}',
);
}
final resultData = Float32List(list1.length);
final simdIterations = list1.length ~/ 4;
for (var simdIndex = 0; simdIndex < simdIterations; simdIndex++) {
final index = simdIndex * 4;
final simdValue = simdOperation(
Float32x4(
list1[index],
list1[index + 1],
list1[index + 2],
list1[index + 3],
),
Float32x4(
list2[index],
list2[index + 1],
list2[index + 2],
list2[index + 3],
),
);
resultData[index] = simdValue.x;
resultData[index + 1] = simdValue.y;
resultData[index + 2] = simdValue.z;
resultData[index + 3] = simdValue.w;
}
for (var index = simdIterations * 4; index < list1.length; index++) {
resultData[index] = remainingOperation(list1[index], list2[index]);
}
return resultData;
}
例えば, 各成分の間の和をとる場合, 次のように実装できます.
simdOperation
の a + b
は Float32x4
に定義されている operator です.
Float32List simdAdd(
Float32List list1,
Float32List list2,
) =>
simdBinaryOperation(
list1,
list2,
simdOperation: (a, b) => a + b,
remainingOperation: (a, b) => a + b,
);
DartPad に実行できるコードを置いておきます. 和以外にも色々試してみてください.
結論
この例のベンチマークなどはとっていませんが, パフォーマンスは向上しているはずです.
このように割と簡単な実装で画像処理などのパフォーマンス向上ができるので, パフォーマンスを改善したい場合の最初の選択肢として有力なのではないでしょうか.
Discussion