Más contenido relacionado
La actualidad más candente (20)
Similar a Boost.SIMD (20)
Más de Akira Takahashi (20)
Boost.SIMD
- 1. Boost.SIMDによる実用的な
SIMDアクセラレーション
Mathias Gaunard Joël Falcou Jean-Thierry Lapresté
MetaScale
Boostcon 2011
翻訳 : 高橋 晶(faithandbrave@gmail.com)
- 2. コンテキスト
コンテキスト
NT2からBoost.SIMDへ
• 昨年、我々はハイパフォーマンスな数値計算のための、Matlabライクな
ProtoベースのライブラリであるNT2のプレゼンテーションを行った
• Boost.SIMDは、SIMDサブコンポーネント抽出のライブラリ
• GSoCプロジェクトが、レビュー準備のために今年の夏に行われる予定
• ここでは、どんな提案をするのかについて話す
- 3. SIMD
SIMDとは何か?
原則
• ひとつの命令、複数
のレジスタ
• ひとつのレジスタの
NxT要素に適用された
操作
• 通常のALU/FPUよりN
倍速い
- 4. SIMDの抽象化
SIMDの抽象化はなぜ必要なのか?
x86ファミリー
• MMX 64-bit float, double PowerPCファミリー
• SSE 128-bit float • AltiVec 128-bit int8, int16,
• SSE2 128-bit int8, int16, int32, int64, float
• int32, int64, double • Cell SPU 128-bit int8, int16,
• SSE3 int32, int64, float, double
• SSSE3
• SSE4a (AMD only)
• SSE4.1
ARMファミリー
• SSE4.2
• AVX 256-bit float, double • VFP 64-bit float, double
• FMA4 (AMD only) • NEON 64-bit and 128-bit
• XOP (AMD only) float, int8, int16, int32, int64
• FMA3
- 5. 明示的なSIMD並列化
コンパイラはなぜそれをしないのか?
コンパイラはとても賢いだけ
• 自動ベクトル化(auto vectorization)は以下のような場合にだけ起こるこ
とができる:
– (何者かの代わりに)メモリを扱う者が決まっている
– コードは本来ベクトル化が可能である
• コンパイルされた関数はベクトル化されない(libmなど)
• コンパイラには、それが何をベクトル化することができるか判
断できるくらいの静的な情報がいつでもあるわけではない
• ベクトル化のための設計は、ヒューマンプロセスである
結論
• 明確にSIMD並列性を宣言するのは、あなたのコードをベクトル化させる最
も良い方法である
• このプレゼンテーションでデモンストレーションを行う
- 7. SIMD手書き
手書きしてみる
32ビット整数のベクトルでa * b + cする : SSE
__m128i a, b, c, result ;
result = _mm_mul_epi32 (a, _mm_add_epi32 (b, c));
32ビット整数のベクトルでa * b + cする : Altivec
__vector int a, b, c, result ;
result = vec_cts ( vec_madd ( vec_ctf (a ,0)
, vec_ctf (b ,0)
, vec_ctf (c ,0)
)
,0);
- 8. パック
パック抽象化
simd::pack<T>
pack<T, N> 型TのN要素をパックするSIMDレジスタ
pack<T> 利用可能な最適なNを自動的に使用する
• TとT以外のパックという例外操作を除いて、それはTのように振舞う。
制約
• Tは基本的な算術型でなければならない。たとえば:(un)signed char,
(unsigned) short, (unsigned) int, (unsigned) long, (unsigned) long long, float
or double - not bool.
• Nは2のべき乗でなければならない
- 9. プリミティブ
パック抽象化
演算子
• オーバーロード可能な全ての演算子が利用できる
• pack<T> × pack<T>操作だけでなくpack<T> × Tも可能
• 型の強制と昇格の無効
uint8_t(255) + uint8_t(1)はint(256)ではなくuint_t(0)となる
比較
• ==, !=, <, <=, >=は、語彙比較(lexical comparison)を行う
• eq, neq, lt, gt, le, ge関数は、boolのpackを返す
その他のプロパティ
• ReadOnlyRandomAccessFusionSequenceとReadOnlyRandomAccessRange
の両方をモデル化する
• at_c<i>(p)もしくはp[i]をアクセスに使用できる。i番目の要素へのアクセス
は便利だが遅い(at_cは速い)
- 10. プリミティブ
pack API
メモリアクセス
pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*N
にアライメントされていなければならない。エラー時の振る舞いは未定義
Examples
- 11. プリミティブ
pack API
メモリアクセス
pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*N
にアライメントされていなければならない。エラー時の振る舞いは未定義
例
load< pack<T, N> >(p, i)はp + i*Nアライメントされたアドレスからloadされる
- 12. プリミティブ
pack API
メモリアクセス
pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*N
にアライメントされていなければならない。エラー時の振る舞いは未定義
例
load< pack<T, N>, Offset>(p, i)はp + i*N + Offsetアライメントされたアドレスから
loadされる 。p + iはアライメントされていなければならない。
- 13. プリミティブ
pack API
メモリアクセス
pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*N
にアライメントされていなければならない。エラー時の振る舞いは未定義
例
store(p, i, pk)はp + i*Nアライメントされたアドレスでpkにstoreする
- 14. プリミティブ
proto entityとしてのpack
論拠
• ほとんどのSIMD ISAはfused操作を持っている(FMA, etc...)
• 簡単なコードを書いて最も良いパフォーマンスを得たい
• 遅延評価が必要 : protoに助けてもらおう!
利点
• 全ての式や関数が、変換演算子で評価されるテンプレート式を生成する
• a * b + cはfma(a, b, c)にマッピングされる
a + b * cはfma(b, c, a)にマッピングされる
!(a < b)はis_nle(a, b)にマッピングされる
• 拡張のための最適化システムは開かれている
- 15. プリミティブ
他の算術、ビット、ieee演算と述語
算術演算 ビット演算 述語
• saturated • select • ゼロとの比較
arithmetic • andnoot, ornot • 比較の否定
• float/int変換 • popcnt • is_unord, is_nan,
• round, floor, ceil, • ffs is_invalid
trunk • ror, rol • is_odd, is_even,
• sqrt, hypot • rshr, rshl • majority
• average • twopower
• random
• min/max IEEE
• rounded division and • ilogb, frexp
remainder • ldexp
• next/prev
• ulpldist
- 16. プリミティブ
ReductionとSWAR演算
Reduction SWAR
• any, all • group / split
• nbtree • slatetd reduction
• minimum/maximum, • cumsum
posmin/posmax • sort
• sum
• product, dot product
- 17. 拡張ポイント
native<T, X> : archのTに関するSIMDレジスタ。X
セマンティクス
• packと似ているが、全ての演算と関数の戻り値がExpression Templateを
返すわけではない。
• Xは命令ではなく使用可能な型の登録。SSEの全てのバリエーションのタグ
だけを指定する。
• ライブラリを拡張するために使用されなければならないのはインタフェース
である。
例
native<float, tag::sse_> は __m128 をラップする
native<uint8_t, tag::sse_> は __m128i をラップする
native<double, tag::avx_> は __m256d をラップする
native<double, tag::altivec_> は __vector float をラップする
- 18. 拡張ポイント
native<T, X> : archのTに関するSIMDレジスタ。X
ソフトウェアフォールバック
• tag::none_<N>は、Nバイトサイズのレジスタによる、ソフトウェアエミュレー
トされたSIMDアーキテクチャ
• 満足のいくSIMDアーキテクチャが見つからない場合にフォールバックとし
て使用する
• これのおかげで、コードのデグレードが汎用的なまま
• SIMDが見つからない場合のデフォルトのネイティブ型 :
native<T, tag::none_<8> >
- 19. RGBをグレースケールにする
RGBをグレースケールにする
スカラバージョン
float const *red, *green, *blue;
float* result;
for (std::size_t i = 0; i != height * width ; ++i)
result[i] = 0.3f * red[i] + 0.59f * green[i] + 0.11f * blue[i];
SIMDバージョン
std::size_t N = meta::cardinal_of<pack<float> >::value;
for (std::size_t i = 0; i != height*width/N; ++i)
{
pack<float> r = load< pack<float> >(red, i);
pack<float> g = load< pack<float> >(green, i);
pack<float> b = load< pack<float> >(blue, i);
pack<float> res = 0.3f * r + 0.59f * g + 0.11f * b;
store(res, result, i);
}
- 20. プリミティブ
簡単だが、どうすれば…
• …RGB or RGBAが混在していたらどうするか?
• …floatではなく8ビット整数だったら?
複雑に聞こえるものは、あとで紹介する。
- 22. 論拠
演算 vs データ
どこで/どのようにデータを格納するか
• SIMD演算はデータ上で動作する必要がある
• 通常のアプローチでは、ユーザーに規定されたコンテナを強制する
• これは十分にジェネリックではない
より良いアプローチ
• SIMD準拠アロケータ
• SIMDのRangeとイテレータ : CountiguousRange
• 標準アルゴリズムのサブセットを操作するためにSIMDクラスをアダプトする
- 23. 論拠
演算 vs データ
どこで/どのようにデータを格納するか
• SIMD演算はデータ上で動作する必要がある
• 通常のアプローチでは、ユーザーに規定されたコンテナを強制する
• これは十分にジェネリックではない
より良いアプローチ
• SIMD準拠アロケータ
• SIMDのRangeとイテレータ : CountiguousRange
• 標準アルゴリズムのサブセットを操作するためにSIMDクラスをアダプトする
- 24. SIMDアロケータ
SIMDアロケータ
論拠
• SIMDに準拠した方法でメモリを処理するコンテナを許可する
• メモリのアライメントをハンドル
• メモリのパディングをハンドル
例
std::vector<float, simd::allocator<float> > v(173);
assert( simd::is_aligned(&v[0]) );
- 27. SIMDイテレータとRange
RangeからSIMD Rangeへ
イテレータインタフェース
• Boost.SIMDは、simd::begin()/simd::end()を提供する
• SIMDイテレータはpackを返して回るイテレータ
• regular rangeをとり、SIMD上でイテレートする
例
std::vector<float, simd::allocator<float> > v(1024);
pack<float> x,z;
x = std::accumulate( simd::begin(v.begin())
, simd::end(v.end())
, z
);
- 28. SIMDイテレータとRange
RangeからSIMD Rangeへ
イテレータインタフェース
• Boost.SIMDは、simd::begin()/simd::end()を提供する
• SIMDイテレータはpackを返して回るイテレータ
• regular rangeをとり、SIMD上でイテレートする
例
std::vector<float, simd::allocator<float> > v(1024);
pack<float> x,z;
x = boost::accumulate( simd::range(v), z );
- 34. SIMDイテレータとRange
SIMD RangeとジェネリックなSIMD/スカラーコード
RGB2Greyに戻る
template <class RangeIn, class RangeOut> inline void
rgb2grey( RangeOut result, RangeIn red, RangeIn green, RangeIn blue )
{
typedef typename RangeIn::iterator in_iterator;
typedef typename RangeOut::iterator iterator;
typedef typename iterator_value< iterator >::type type;
iterator br = result.begin(), er = result.end();
in_iterator r = red.begin();
in_iterator g = green.begin();
in_iterator b = blue.begin();
while ( br != er )
{
type rv = load< type >(r, 0);
type gv = load< type >(g, 0);
type bv = load< type >(b, 0);
type res = 0.3f * rv + 0.59f * gv + 0.11f * bv;
store(res, br, 0);
br++; r++; g++; b++;
}
}
- 35. SIMDイテレータとRange
何が足りないか
統合されたSIMDサポート
• ほとんどの標準アルゴリズムが、一息で実行できるように特殊化されるべ
きだ
• simd(r)のようなRangeアダプタができるだろうか?
• load<T, N>を使用したshifted Rangeのサポート
いくつかのSIMDによる頭の体操
• SIMD find?
• SIMD sort?
• copyのような高速化?
- 45. シフトした読み込み
ベクトルの解決策
SIMD/スカラーバージョン
template < class RangeIn, class RangeOut >
inline void average( RangeOut result, RangeIn input )
{
typedef typename RangeIn::iterator in_iterator;
typedef typename RangeOut::iterator iterator;
typedef typename iterator_value< iterator >::type type;
iterator br = result.begin(), er = result.end();
in_iterator data = input.begin();
br++; er--;
while ( br != er )
{
type xm1 = load<type, -1>(data, i);
type x = load<type>(data, i);
type xp1 = load<type, +1>(data, i);
store(res, i, 0) = 1.f/3 * (xm1 + x + xp1);
}
}
- 46. 昇格と飽和(Promotion and Saturation)
RGB2Greyへ戻る
8ビットRGB
static const std::size_t N = meta::cardinal_of< pack<uint8_t> >::value;
for (std::size_t i = 0; i != height*width/N; ++i)
{
pack<uint8_t> r = load< pack<uint8_t> >(red, i);
pack<uint8_t> g = load< pack<uint8_t> >(green, i);
pack<uint8_t> b = load< pack<uint8_t> >(blue, i);
pack<uint8_t> res = uint8_t(77) * r / uint8_t(255) + uint8_t(150)
* g / uint8_t(255) + uint8_t(28) * b / uint8_t(255);
store(res, result, i);
}
- 47. 昇格と飽和(Promotion and Saturation)
RGB2Greyへ戻る
packの昇格
uint16_t r_coeff = 77;
uint16_t g_coeff = 150;
uint16_t b_coeff = 28;
uint16_t div_coeff = 255;
pack<uint16_t> r1, r2, g1, g2, b1, b2;
tie(r1, r2) = split(r);
tie(g1, g2) = split(g);
tie(b1, b2) = split(b);
pack<uint16_t> res1 = (r_coeff * r1 + g_coeff * g1 + b_coeff * b1) / div_coeff;
pack<uint16_t> res2 = (r_coeff * r2 + g_coeff * g2 + b_coeff * b2) / div_coeff;
pack<uint8_t> res = group(res1, res2);
- 49. Boost.SIMDの概要
proto entityとしてのpack
我々の目標
• SIMDプログラミングに、使いやすい状態をもたらす
• もしboost.atomicがあったら、boost.simdをどうするか?
• C++の残りのすばらしいものを使って、さらに魅力的にする
我々が達成したいこと
• NT2で学んだことの活用する
• パフォーマンス用語のいくつかの影響を実証する
• スカラーを使用する場合と同じくらい単純なSIMD命令にする
- 50. 今後の活動
Google Summer of Code 2011
• 混乱をクリーンナップし、boostifyにする
• STL/Boostとの互換性を向上させる
• 募集:このライブラリを実際に活用したアプリケーション