51
Mathias Gaunard Joël Falcou Jean-Thierry Lapresté MetaScale Boostcon 2011 翻訳 : 高橋 ([email protected]) Boost.SIMDによる実用的な SIMDアクセラレーション

Boost.SIMD

Embed Size (px)

DESCRIPTION

BoostCon 2011でのJoel Falcou, Mathias Gaunardによる発表「Practical SIMD acceleration with Boost.SIMD」の日本語訳。

Citation preview

Page 1: Boost.SIMD

Mathias Gaunard Joël Falcou Jean-Thierry Lapresté

MetaScale

Boostcon 2011

翻訳 : 高橋 晶([email protected])

Boost.SIMDによる実用的な SIMDアクセラレーション

Page 2: Boost.SIMD

コンテキスト

• 昨年、我々はハイパフォーマンスな数値計算のための、MatlabライクなProtoベースのライブラリであるNT2のプレゼンテーションを行った

• Boost.SIMDは、SIMDサブコンポーネント抽出のライブラリ

• GSoCプロジェクトが、レビュー準備のために今年の夏に行われる予定

• ここでは、どんな提案をするのかについて話す

コンテキスト

NT2からBoost.SIMDへ

Page 3: Boost.SIMD

SIMD

• ひとつの命令、複数のレジスタ

• ひとつのレジスタのNxT要素に適用された操作

• 通常のALU/FPUよりN倍速い

SIMDとは何か?

原則

Page 4: Boost.SIMD

SIMDの抽象化

• AltiVec 128-bit int8, int16, int32, int64, float

• Cell SPU 128-bit int8, int16, int32, int64, float, double

SIMDの抽象化はなぜ必要なのか?

PowerPCファミリー • MMX 64-bit float, double • SSE 128-bit float • SSE2 128-bit int8, int16, • int32, int64, double • SSE3 • SSSE3 • SSE4a (AMD only) • SSE4.1 • SSE4.2 • AVX 256-bit float, double • FMA4 (AMD only) • XOP (AMD only) • FMA3

x86ファミリー

• VFP 64-bit float, double

• NEON 64-bit and 128-bit float, int8, int16, int32, int64

ARMファミリー

Page 5: Boost.SIMD

明示的なSIMD並列化

• 自動ベクトル化(auto vectorization)は以下のような場合にだけ起こることができる:

– (何者かの代わりに)メモリを扱う者が決まっている

– コードは本来ベクトル化が可能である

• コンパイルされた関数はベクトル化されない(libmなど)

• コンパイラには、それが何をベクトル化することができるか判断できるくらいの静的な情報がいつでもあるわけではない

• ベクトル化のための設計は、ヒューマンプロセスである

コンパイラはなぜそれをしないのか?

コンパイラはとても賢いだけ

• 明確にSIMD並列性を宣言するのは、あなたのコードをベクトル化させる最も良い方法である

• このプレゼンテーションでデモンストレーションを行う

結論

Page 6: Boost.SIMD

Talk Layout

1. はじめに

2. インタフェース

3. SIMDとSTL

4. SIMD仕様イディオム

5. 結論

Page 7: Boost.SIMD

SIMD手書き

__m128i a, b, c, result ;

result = _mm_mul_epi32 (a, _mm_add_epi32 (b, c));

手書きしてみる

32ビット整数のベクトルでa * b + cする : SSE

__vector int a, b, c, result ; result = vec_cts ( vec_madd ( vec_ctf (a ,0) , vec_ctf (b ,0) , vec_ctf (c ,0) ) ,0);

32ビット整数のベクトルでa * b + cする : Altivec

Page 8: Boost.SIMD

パック

パック抽象化

pack<T, N> 型TのN要素をパックするSIMDレジスタ

pack<T> 利用可能な最適なNを自動的に使用する

• TとT以外のパックという例外操作を除いて、それはTのように振舞う。

simd::pack<T>

• Tは基本的な算術型でなければならない。たとえば:(un)signed char, (unsigned) short, (unsigned) int, (unsigned) long, (unsigned) long long, float or double - not bool.

• Nは2のべき乗でなければならない

制約

Page 9: Boost.SIMD

プリミティブ

パック抽象化

• オーバーロード可能な全ての演算子が利用できる

• 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は速い)

その他のプロパティ

Page 10: Boost.SIMD

プリミティブ

pack API

pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*Nにアライメントされていなければならない。エラー時の振る舞いは未定義

メモリアクセス

Examples

Page 11: Boost.SIMD

プリミティブ

pack API

pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*Nにアライメントされていなければならない。エラー時の振る舞いは未定義

メモリアクセス

load< pack<T, N> >(p, i)はp + i*Nアライメントされたアドレスからloadされる

Page 12: Boost.SIMD

プリミティブ

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はアライメントされていなければならない。

Page 13: Boost.SIMD

プリミティブ

pack API

pack<T, N>からT*、もしくはその逆にload/storeを行うには、メモリはsizeof(T)*Nにアライメントされていなければならない。エラー時の振る舞いは未定義

メモリアクセス

store(p, i, pk)はp + i*Nアライメントされたアドレスでpkにstoreする

Page 14: Boost.SIMD

プリミティブ

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)にマッピングされる

• 拡張のための最適化システムは開かれている

利点

Page 15: Boost.SIMD

プリミティブ

他の算術、ビット、ieee演算と述語

• saturated arithmetic

• float/int変換

• round, floor, ceil, trunk

• sqrt, hypot

• average

• random

• min/max

• rounded division and remainder

算術演算

• select

• andnoot, ornot

• popcnt

• ffs

• ror, rol

• rshr, rshl

• twopower

ビット演算

• ilogb, frexp

• ldexp

• next/prev

• ulpldist

IEEE

• ゼロとの比較

• 比較の否定

• is_unord, is_nan, is_invalid

• is_odd, is_even,

• majority

述語

Page 16: Boost.SIMD

プリミティブ

ReductionとSWAR演算

• any, all

• nbtree

• minimum/maximum, posmin/posmax

• sum

• product, dot product

Reduction

• group / split

• slatetd reduction

• cumsum

• sort

SWAR

Page 17: Boost.SIMD

拡張ポイント

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 をラップする

Page 18: Boost.SIMD

拡張ポイント

native<T, X> : archのTに関するSIMDレジスタ。X

• tag::none_<N>は、Nバイトサイズのレジスタによる、ソフトウェアエミュレートされたSIMDアーキテクチャ

• 満足のいくSIMDアーキテクチャが見つからない場合にフォールバックとして使用する

• これのおかげで、コードのデグレードが汎用的なまま

• SIMDが見つからない場合のデフォルトのネイティブ型 : native<T, tag::none_<8> >

ソフトウェアフォールバック

Page 19: Boost.SIMD

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];

スカラバージョン

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);

}

SIMDバージョン

Page 20: Boost.SIMD

プリミティブ

簡単だが、どうすれば…

• …RGB or RGBAが混在していたらどうするか?

• …floatではなく8ビット整数だったら?

複雑に聞こえるものは、あとで紹介する。

Page 21: Boost.SIMD

Talk Layout

1. はじめに

2. インタフェース

3. SIMDとSTL

4. SIMD仕様イディオム

5. 結論

Page 22: Boost.SIMD

論拠

演算 vs データ

• SIMD演算はデータ上で動作する必要がある

• 通常のアプローチでは、ユーザーに規定されたコンテナを強制する

• これは十分にジェネリックではない

どこで/どのようにデータを格納するか

• SIMD準拠アロケータ

• SIMDのRangeとイテレータ : CountiguousRange

• 標準アルゴリズムのサブセットを操作するためにSIMDクラスをアダプトする

より良いアプローチ

Page 23: Boost.SIMD

論拠

演算 vs データ

• SIMD演算はデータ上で動作する必要がある

• 通常のアプローチでは、ユーザーに規定されたコンテナを強制する

• これは十分にジェネリックではない

どこで/どのようにデータを格納するか

• SIMD準拠アロケータ

• SIMDのRangeとイテレータ : CountiguousRange

• 標準アルゴリズムのサブセットを操作するためにSIMDクラスをアダプトする

より良いアプローチ

Page 24: Boost.SIMD

SIMDアロケータ

SIMDアロケータ

• SIMDに準拠した方法でメモリを処理するコンテナを許可する

• メモリのアライメントをハンドル

• メモリのパディングをハンドル

論拠

std::vector<float, simd::allocator<float> > v(173);

assert( simd::is_aligned(&v[0]) );

Page 25: Boost.SIMD

SIMDイテレータとRange

RangeからSIMD Rangeへ

• Boost.SIMDは、simd::begin()/simd::end()を提供する

• SIMDイテレータはpackを返して回るイテレータ

• regular rangeをとり、SIMD上でイテレートする

イテレータインタフェース

Examples

Page 26: Boost.SIMD

SIMDイテレータとRange

RangeからSIMD Rangeへ

• Boost.SIMDは、simd::begin()/simd::end()を提供する

• SIMDイテレータはpackを返して回るイテレータ

• regular rangeをとり、SIMD上でイテレートする

イテレータインタフェース

Page 27: Boost.SIMD

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

);

Page 28: Boost.SIMD

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 );

Page 29: Boost.SIMD

SIMDイテレータとRange

RangeからSIMD Rangeへ

• nativeとpackは、begin()/end()を提供する

• 標準アルゴリズムを直接使用可能

• Boost.Rangeアルゴリズムを直接使用可能

イテレータインタフェース

pack<float> x(1, 2, 3, 4);

float k = std::accumulate(x.begin() x.end(), 0.f );

Page 30: Boost.SIMD

SIMDイテレータとRange

RangeとしてのSIMD値

全てをまとめる

std::vector<float, simd::allocator<float> > v(1024);

pack<float> x, z;

float r;

x = boost::accumulate(simd::range(v), z);

r = std::accumulate(x.begin(), x.end(), 0.f);

Page 31: Boost.SIMD

SIMDイテレータとRange

RangeとしてのSIMD値

全てをまとめる – より良いバージョン

std::vector<float, simd::allocator<float> > v(1024);

float r;

r = sum(accumulate(simd::range(v), pack<float>()));

Page 32: Boost.SIMD

SIMDイテレータとRange

RangeとしてのSIMD値

doubleでのstd::accumulateの高速化

Page 33: Boost.SIMD

SIMDイテレータとRange

RangeとしてのSIMD値

floatでのstd::accumulateの高速化

Page 34: Boost.SIMD

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++;

}

}

Page 35: Boost.SIMD

SIMDイテレータとRange

何が足りないか

• ほとんどの標準アルゴリズムが、一息で実行できるように特殊化されるべきだ

• simd(r)のようなRangeアダプタができるだろうか?

• load<T, N>を使用したshifted Rangeのサポート

統合されたSIMDサポート

• SIMD find?

• SIMD sort?

• copyのような高速化?

いくつかのSIMDによる頭の体操

Page 36: Boost.SIMD

Talk Layout

1. はじめに

2. インタフェース

3. SIMDとSTL

4. SIMD仕様イディオム

5. 結論

Page 37: Boost.SIMD

条件ハンドリング

SIMDでの真理値

問題

pack<float> x(1 ,2 ,3 ,4);

pack<float> c(2.5);

cout << lt(x,c) << endl;

Page 38: Boost.SIMD

条件ハンドリング

SIMDでの真理値

問題

pack<float> x(1 ,2 ,3 ,4);

pack<float> c(2.5);

cout << lt(x,c) << endl;

(( Nan Nan 0 0 ))

Page 39: Boost.SIMD

条件ハンドリング

SIMDでの真理値

問題

pack<float> x(1 ,2 ,3 ,4);

pack<float> c(2.5);

cout << lt(x,c) << endl;

(( Nan Nan 0 0 ))

解決策

Tに適切なtrue値w/rを返すTrue<T>()

Tに適切なfalse値w/rを返すFalse<T>()

Page 40: Boost.SIMD

条件ハンドリング

SIMDでの条件式

// スカラーコード

if( x > 4 )

y = 2*x;

else

z = 1.f/x;

// SIMD code

// ???

Page 41: Boost.SIMD

条件ハンドリング

SIMDでの条件式

// スカラーコード

if( x > 4 )

y = 2*x;

else

z = 1.f/x;

// SIMD code

y = where( gt(x, 4), 2*x, y);

z = where( gt(x, 4), z, 1.f/x);

Page 42: Boost.SIMD

シフトした読み込み

動機

Page 43: Boost.SIMD

シフトした読み込み

着々と進んでいる…

Page 44: Boost.SIMD

シフトした読み込み

ベクトルの解決策

Page 45: Boost.SIMD

シフトした読み込み

ベクトルの解決策

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);

}

}

Page 46: Boost.SIMD

昇格と飽和(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);

}

Page 47: Boost.SIMD

昇格と飽和(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);

Page 48: Boost.SIMD

Talk Layout

1. はじめに

2. インタフェース

3. SIMDとSTL

4. SIMD仕様イディオム

5. 結論

Page 49: Boost.SIMD

Boost.SIMDの概要

proto entityとしてのpack

• SIMDプログラミングに、使いやすい状態をもたらす

• もしboost.atomicがあったら、boost.simdをどうするか?

• C++の残りのすばらしいものを使って、さらに魅力的にする

我々の目標

• NT2で学んだことの活用する

• パフォーマンス用語のいくつかの影響を実証する

• スカラーを使用する場合と同じくらい単純なSIMD命令にする

我々が達成したいこと

Page 50: Boost.SIMD

今後の活動

• 混乱をクリーンナップし、boostifyにする

• STL/Boostとの互換性を向上させる

• 募集:このライブラリを実際に活用したアプリケーション

Google Summer of Code 2011

Page 51: Boost.SIMD

Thanks for your attension