- 【C++】高速化手法【SSE】
932 :デフォルトの名無しさん[sage]:2015/05/20(水) 00:03:13.46 ID:Uv5CefPe - 初心者ですみません。
画像のアルファブレンディング付きコピーを行いたいです。 asm{}ではなく、なんて言うのかは知りませんが、xmmintrin.hに載ってる組み込み関数を使いたいです。 サンプルソースが載っているお勧めサイトはありますか?英語でもかまいません。 また、画像のアルファブレンディング付きコピーのような処理は、SSEで速くなるでしょうか? どうにもメモリがボトルネックになりそうな気がしてまして。 キャッシュのプリフェッチなんかも必要でしょうか?
|
- 【C++】高速化手法【SSE】
934 :デフォルトの名無しさん[sage]:2015/05/20(水) 00:15:39.94 ID:Uv5CefPe - レスサンクス。
速くなるならチャレンジしてみようかしら。
|
- 【C++】高速化手法【SSE】
935 :デフォルトの名無しさん[sage]:2015/05/20(水) 00:33:47.38 ID:Uv5CefPe - 画像のsrcとdstは32bitのA8R8G8B8で、
これをSIMDでα付き合成したいわけですが、 この場合、MMXを使うべきなのでしょうか? 個人的にはSSE2あたりを使いたいのですが。 __m128だとfloat[4]なので、一度に1dot分ずつしか処理できないんですかね? 128bit長もあるのに。 __m128の定義を見ると、 unsigned __int8 m128_u8[16]; ってのがあるので、8bitの整数処理もサポートしてそうなんですが、 xmmintrin.hを見る限り、使えそうな関数が無いのですが、 どうすればよいんでしょうか?
|
- 【C++】高速化手法【SSE】
936 :デフォルトの名無しさん[sage]:2015/05/20(水) 00:58:40.19 ID:Uv5CefPe - __m128i _mm_adds_epi8(__m128i a, __m128i b);
8bitの飽和加算はみつけたんだが、乗算が見つからん。たすけてくり〜〜
|
- 【C++】高速化手法【SSE】
937 :デフォルトの名無しさん[sage]:2015/05/20(水) 01:10:31.88 ID:Uv5CefPe - __m128 _mm_cvtpu8_ps(__m64 a)
もうよく分からないので↑つかってfloat[4]に変換して 1dotずつ計算します。 整数演算でよいのに浮動小数点使うのはなんか釈然としませんが。
|
- 【C++】高速化手法【SSE】
940 :デフォルトの名無しさん[sage]:2015/05/20(水) 02:10:09.47 ID:Uv5CefPe - auto &d = dst; auto &s = src;
__m64 d64, s64; d64.m64_u32[0] = d; s64.m64_u32[0] = s; __m128 d128 = _mm_cvtpu8_ps( d64 ); __m128 s128 = _mm_cvtpu8_ps( s64 ); float f256 = 256.0f, finv256 = 1.0f/256.0f, f1 = 1.0f; __m128 _1 = _mm_load1_ps( &f1 ); __m128 _256 = _mm_load1_ps( &f256 ); __m128 _inv256 = _mm_load1_ps( &finv256 ); d128 = _mm_mul_ps( d128, _inv256 ); s128 = _mm_mul_ps( s128, _inv256 ); __m128 a128 = _mm_load1_ps( s128.m128_f32+3 ); __m128 _1_sub_a128 = _mm_sub_ps( _1, a128 ); d128 = _mm_mul_ps( d128, _1_sub_a128 ); s128 = _mm_mul_ps( s128, a128 ); d128 = _mm_add_ps( d128, s128 ); d128 = _mm_mul_ps( d128, _256 ); d64 = _mm_cvtps_pi16( d128 ); auto ar = d64.m64_u16; d = (0xFF<<24)|(ar[2]<<16)|(ar[1]<<8)|(ar[0]); 一応出来ました。まだ飽和処理してませんが。速くなったかどうかは分かりませぬ。
|
- 【C++】高速化手法【SSE】
942 :940[sage]:2015/05/20(水) 02:33:37.51 ID:Uv5CefPe - 上記のコードですが、SSEを使わない下記のコードと比べてとても遅いです。当然Releaseでです。
auto &d = dst; auto &s = src; uint32_t a = s>>24; uint32_t dr = 0xFF&(d>>16); uint32_t dg = 0xFF&(d>>8); uint32_t db = 0xFF&d; uint32_t sr = 0xFF&(s>>16); uint32_t sg = 0xFF&(s>>8); uint32_t sb = 0xFF&s; uint32_t r = ( dr * (255-a) + sr * a )>>8; uint32_t g = ( dg * (255-a) + sg * a )>>8; uint32_t b = ( db * (255-a) + sb * a )>>8; d = (0xFF<<24)|(r<<16)|(g<<8)|(b); 私の努力は無意味だったようです。
|
- 【C++】高速化手法【SSE】
945 :デフォルトの名無しさん[sage]:2015/05/20(水) 03:48:44.67 ID:Uv5CefPe - floatの計算と定数のセットをループの外部に追いやってもやっぱり遅いです。
私が思うに、いくら並列計算をするといっても、 たかが整数8bitの計算に浮動小数点を使うのは馬鹿げていたのではないかと。 おそらくですが、整数⇔浮動小数点 の変換が重たいのではないかと想像しています。 画像のアルファブレンディングのような処理は、MMXですべきだったのかもしれません。 _mm_mulhi_pi16を使ってみようかと思います。
|
- 【C++】高速化手法【SSE】
948 :940[sage]:2015/05/20(水) 04:44:30.33 ID:Uv5CefPe - __m64 d64, s64, a64, b64;
d64.m64_u16[0] = 0xFF&(d_>>16);d64.m64_u16[1] = 0xFF&(d_>>8);d64.m64_u16[2] = 0xFF&(d_>>0); s64.m64_u16[0] = 0xFF&(s_>>16);s64.m64_u16[1] = 0xFF&(s_>>8);s64.m64_u16[2] = 0xFF&(s_>>0); a64.m64_u16[0] = 0xFF&(s_>>24);a64.m64_u16[1] = 0xFF&(s_>>24);a64.m64_u16[2] = 0xFF&(s_>>24); b64 = _mm_subs_pu16( _255, a64 ); d64 = _mm_mullo_pi16( d64, b64 ); s64 = _mm_mullo_pi16( s64, a64 ); d64 = _mm_add_pi16( d64, s64 ); uint32_t r = d64.m64_u16[0]>>8; uint32_t g = d64.m64_u16[1]>>8; uint32_t b = d64.m64_u16[2]>>8; d = (0xFF<<24)|(r<<16)|(g<<8)|(b); 浮動小数点演算を使わないように、上記のように書き直してみましたが、 >>942のコードよりも3倍遅くなりました。もう嫌です。
|
- 【C++】高速化手法【SSE】
949 :デフォルトの名無しさん[sage]:2015/05/20(水) 04:52:06.80 ID:Uv5CefPe - 画像の合成はDirectXさんに頑張ってもらうことにします。
|
- 【C++】高速化手法【SSE】
964 :940[sage]:2015/05/20(水) 14:46:38.07 ID:Uv5CefPe - あれからまた少し頑張ってました。
__m64 d64, s64, a64, b64; d64.m64_u64 = *d; d64 = _mm_unpacklo_pi8( d64, _0 ); s64.m64_u64 = *s; s64 = _mm_unpacklo_pi8( s64, _0 ); uint32_t a = (0xFF000000&(*s))>>24; a64.m64_u32[0] = a|(a<<8)|(a<<16); a64 = _mm_unpacklo_pi8( a64, _0 ); b64 = _mm_subs_pu16( _255, a64 ); d64 = _mm_mullo_pi16( d64, b64 ); s64 = _mm_mullo_pi16( s64, a64 ); d64 = _mm_add_pi16( d64, s64 ); d64 = _mm_srli_pi16( d64, 8 ); d64 = _mm_packs_pu16( d64, s64 ); *d = d64.m64_u32[0]; 最初のころの>>940や>>948のコードと比べるとだいぶ良くなってきましたが、 それでも何も考えずに書いた>>942のコードよりも2倍遅いです。 3倍から2倍になったので進歩はしましたが・・・。
|
- 【C++】高速化手法【SSE】
965 :940[sage]:2015/05/20(水) 15:07:11.63 ID:Uv5CefPe - __m128を使えば2dot同時に処理できるのでもう少し速くなるかもしれませんが、
>>964の時点で何も考えずに書いたコード>>942よりも2倍遅いので、 このまま__m128化しただけではどうにもなりませんね。
|
- 【C++】高速化手法【SSE】
968 :デフォルトの名無しさん[sage]:2015/05/20(水) 15:23:09.81 ID:Uv5CefPe - いい組み込み関数が見つかったので、アルファ値のセットの部分を変更しました。
ちっとも速くなりませんでしたが。 __m64 d64, s64, a64, b64; d64.m64_u64 = *d; d64 = _mm_unpacklo_pi8( d64, _0 ); s64.m64_u64 = *s; s64 = _mm_unpacklo_pi8( s64, _0 ); uint16_t a = (0xFF000000&(*s))>>24; a64 = _mm_set1_pi16( (short)a ); b64 = _mm_subs_pu16( _255, a64 ); d64 = _mm_mullo_pi16( d64, b64 ); s64 = _mm_mullo_pi16( s64, a64 ); d64 = _mm_add_pi16( d64, s64 ); d64 = _mm_srli_pi16( d64, 8 ); d64 = _mm_packs_pu16( d64, _0 ); *d = d64.m64_u32[0];
|
- 【C++】高速化手法【SSE】
973 :デフォルトの名無しさん[sage]:2015/05/20(水) 15:37:34.28 ID:Uv5CefPe - それでちょっと考えたのですが、今のCPUはすっごいスーパースカラーが有るので、
今回のようにrgb各要素は依存関係が無いが、1dot全体では処理順に依存関係が出てしまっている場合、 SIMD化は意味が無いのかもしれません。 >>942のコードは、rgb各要素に対してスーパースカラーが働く。 SIMD化した>>968は、rgb各要素はSIMD化しているし、 演算に依存関係があるのでスーパースカラーの働く余地は無い。 よって、SIMD化のオーバーヘッドだけが付きまとうと。 なので、SIMD化した後もスーパースカラが働くように、 並列化の方向を array of structure から structure of array に変える必要があるのかもしれません。 つまり、mr = ( r1,r2,r3r,4 ), mg = ( g1, g2, g3, g4 ), mb = ( b1,b2,b3,b4 ) として、 1ループで4dot同時に処理すると。こうすればスーパースカラーが働きますよね。 ただ、もっと単純に、ループアンローリングして、複数のピクセルの処理をまぜこぜにして書いて、 スーパースカラーを促したほうが、楽そうです。どちらが良いでしょうか? >>967 それは修正しました。
|
- 【C++】高速化手法【SSE】
978 :デフォルトの名無しさん[sage]:2015/05/20(水) 16:08:36.79 ID:Uv5CefPe - _mm_unpacklo_pi8ってそんなに遅いんですか?
|
- 【C++】高速化手法【SSE】
982 :940[sage]:2015/05/20(水) 16:49:29.01 ID:Uv5CefPe - だんごさんの方式で適当に書いた>>942のコードより、4倍速くなりました!!
ありがとうございました。大変勉強になりました。4倍は凄いことです
|
- 【C++】高速化手法【SSE】
983 :940[sage]:2015/05/20(水) 17:15:33.68 ID:Uv5CefPe - でも _mm_mullo_epi32 は SSE4からなんだよねぇ。
Core2の途中ぐらいから対応か・・・。 _mm_mullo_epi32を使わないとなると、あんま綺麗にまとまらんよねぇ。 どうしたものか。
|
- 【C++】高速化手法【SSE】
993 :940[sage]:2015/05/20(水) 19:43:33.43 ID:Uv5CefPe - _mm_mullo_epi32 が SSE4からなのが嫌だったので、
いったんfloatに変換してから掛け算してまた戻すようにした。 時間が1.18倍になった。 また、_mm_castps_si128を二回使う方法で代用してみた。 時間がが1.39倍になった。
|
- 【C++】高速化手法【SSE】
994 :940[sage]:2015/05/20(水) 19:56:59.95 ID:Uv5CefPe - _mm_packs_epi32
ほうほうこんないいものがあったのか。 8dot一気に処理できそうな予感
|
- 【C++】高速化手法【SSE】
996 :940[sage]:2015/05/20(水) 21:24:41.69 ID:Uv5CefPe - とりあえず、だいんごさんの方法で、読み込んで、
uint32_t[4]をuint16_t[8]にpackする方法で、元の適当なコード比で5倍近くの速度が出たので、 これで良しとします。
|