TOP カテ一覧 スレ一覧 100〜終まで 2ch元 削除依頼
【会津】パソコン甲子園2004【若松】
なぜ「staticおじさん」は叩かれたのか?
Microsoft Silverlight その9
Gitをより良くするための運用ガイドライン作成スレ
WPF(.NET4.x, .NET Core) GUIプログラミング Part23
Pythonのお勉強 Part57
【統計分析】機械学習・データマイニング25
オナオナ開発プロジェクト
プログラム板へのID導入の投票実施中 月曜0:00まで
ふらっと C#,C♯,C#(初心者用) Part146

【C++】高速化手法【SSE】2


1 :2015/05/21 〜 最終レス :2020/03/25
C++やインラインアセンブラ、SSEなどによる高速化の手法
について語りましょう。

前スレ
【C++】高速化手法【SSE】
http://peace.2ch.sc/test/read.cgi/tech/1130349336/

2 :
>>1乙。

10年の時を経て2スレ目に突入、おめでとう。

3 :
1スレ10年も掛かったのかwww

compiler intrinsicsの普及と、x64ではインラインアセンブラが使えなくなったのが
またSIMDの再普及に繋がる気がする

SSE3、SSE4、AVX、AVX2、FMAと言った新しい命令も増えているのも理由の一つか

4 :
それとこれテンプレに入れるか
次スレ今度いつになるか分からないしなww

http://www.officedaytime.com/tips/simd.html

5 :
あと面白そうなところはここか

https://github.com/herumi/x86opti

6 :
10年wwww

7 :
マルチコアによるマルチスレッド化の方がずっと楽だから、難しいSIMDプログラミングはいまいち
はやらないのかな

8 :
SIMDは作るのは難しいが、一度作ってしまうと、関数の中に封じ込めることが出来るので、
後からの使い勝手自体はマルチスレッドよりも良いんだがな。ライブラリ化もただの関数なので問題ない。
マルチスレッドだと関数の中だけで閉じなくて、アプリ全体の設計の部分まで話が広がるので、
機能単位でライブラリ化するのが難しく、簡単に使いまわせない。
ま、SIMDはライブラリ化しやすいから、多くのものが既にライブラリ化されてて、
ほとんどの人はそれを使うだけですむので、自分で書く必要性が少なくて、
故に過疎りやすいってのもあると思うがね。

9 :
SIMD化はコンパイラに任せるのが得策。
アルゴリズム改善がどこもできなくなってからでいい。

10 :
前スレの8bit単位の乗算とか、そういう頻繁にありそうなケースに対応した命令を用意してくれないと、
間口広がんないよ・・・。

11 :
8/16bitぐらいのIndex範囲でテーブル引きしれくれる命令がほしいわ

8bit演算するにも無駄なシャッフルしなくても良い命令がそろったのがSSE4辺りで
さらにAMDが未対応みたいな期間長すぎたな

12 :
命令に対応してたってそれを実行するユニットがそれ相応に強化されてなければ大して効果ないよ

13 :
並列計算が実装されてないか、使っても遅いとき、通常の演算で並列できないか?

(a0 + a1X + a2X^2 + a3X^3) ( s + tX) のXの1乗、3乗を取り出すことで、

14 :
途中送信した。
上のXの項は、t a0 + s a1
上のX^3の項は、t a2 + s a3

Xをたとえば2^10や2^16と取り、下位からの桁上りがないとすると、ビット演算でデータ取り出せる。

15 :
>>13>>14を実装して並列計算の叩き台を作った。微妙に速度アップした。結果が正しくないが計算量は正しいはず。

#include <iostream>
#include <time.h>
using namespace std;
#define N (1<<18)
void initdata(unsigned char *a, int range){ for(int n=0; n<2*range; n+=2) { a[n]=rand()%256; a[n+1]=rand()%256; }}

#define cal(x,y,s) {x=(x*s + y*(255-s))&255; y=(y*s + x*(255-s))&255;}
void test(unsigned char *a) {
for(int n=0; n<2*N; n+=2) {
unsigned char x=a[n], y=a[n+1];
for(int s=1; s<255; s++) cal(x,y,s); for(int s=1; s<255; s++) cal(x,y,s); for(int s=1; s<255; s++) cal(x,y,s); for(int s=1; s<255; s++) cal(x,y,s);
a[n]=x; a[n+1]=y; }}

// (a0 + a1X + a2X^2 + a3X^3) ( 255-s + sX) Xの項は、s*a0 + (255-s)*a1 X^3の項は、s*a2 + (255-s)*a3
#define dset(x,y) (x) + ((y)<<8) + ((y)<<16) + ((x)<<24);
#define cal2(x,s) { x *= ( s + ((255-s)<<8) ); unsigned char c=(x>>8)&255, d=(x>>24)&255; x = dset(c,d); }
void test2(unsigned char *a) {
for(int n=0; n<2*N; n+=2) {
unsigned int x = dset(a[n], a[n+1]);
for(int s=1; s<255; s++) cal2(x,s); for(int s=1; s<255; s++) cal2(x,s); for(int s=1; s<255; s++) cal2(x,s); for(int s=1; s<255; s++) cal2(x,s);
a[n]=(x>>8)&255; a[n+1]=(x>>24)&255; }}

int main() {
unsigned char *a = new unsigned char [2*N],*b = new unsigned char [2*N];
initdata(a, N); memcpy(b,a,2*N);
cout<<"memcmp(a,b):"<<(memcmp(a,b,2*N)?"false":"true")<<endl;
unsigned long t=clock(); test(a); t=clock()-t; cout<<t<<endl;
t=clock(); test2(b); t=clock()-t; cout<<t<<endl;
cout<<"memcmp(a,b):"<<(memcmp(a,b,2*N)?"false":"true")<<endl;
return 0; }

16 :
前スレ終わりごろのビットマップ合成の話だが、結果的にはスカラ版と比較して
5倍程度に収まっていたが、初期はスカラ版より遅い結果しか出せていなかった。
スカラ版と等速なら話はわかるが、
なぜ遅くなるのか?
俺も似たようなこと遭遇した記憶はあるが
詳細が思い出せない。

17 :
SIMD関連はまず命令の実行に都合のいいようにデータを作成するのと
結果を取り出すのに時間がかかるからじゃね?
それとSSEレジスタは128bitなので1処理当たり32bit使うと4並列にしかならない
AVX/AVX2なら256bitなのでこちらの方が速度が上がると思う

18 :
>>16
構造体へのコピーをスカラで何倍もの時間かけてやってたから
本体処理が4秒が1秒に縮まったところでセットアップに5秒かかってたら意味がない

ボトルネックを解消するつもりでSIMDを使ったつもりが新たな別のボトルネックを作ってました
しかもなんで遅くなったのか使った本人が認識してないというの。

19 :
つまりSIMDレジスタで計算する為に
スカラ以上に命令を使った為
遅くなったということか。
前スレの968では見直ししたが2倍ほど遅いと言っていた。
本来4倍になるはずが1/2倍、
8倍も遅くなるなんてありえるか?
根本的に何か問題があるとしか
思えん。

20 :
あんなのソースコード見ればボトルネックがどこにあるか一発でわかるだろ
見ただけでわからんやつは向いてないよ

21 :
「ハッカーのたのしみ」とかの良著をよく読むことをすすめるよ
並列化アルゴリズムに理解のないやつがいきなりSSEやAVX使っても不幸にしかならない

22 :
然しものSIMDも、まったくオーバーヘッドがないわけじゃぁない。
SIMDに演算させるためのデータ構造整えなんかの命令分がオーバーヘッドになる。
これを最小に抑えつつ、SIMDの並列演算性能を活かすのが腕の見せ所よ。

23 :
メモリクソ余ってんだから最初からSIMD前提なデータ構造強制できりゃいいのになぁ

24 :
すいません。以下のようなことがやりたいのですがどうするのが一番いいですか。

void up_index(short a[16],short b[16])
{

25 :
すいません。途中で書き込みました。
avx2でお願いします。

void up_index(short a[16],short b[16])
{
for(int i=0;i<15;i++)
{
b[i]=a[i+1];
}
b[15]=0;
}

void down_index(short a[16],short b[16])
{
b[0]=0;
for(int i=1;i<16;i++)
{
b[i]=a[i-1];
}
}

26 :
void up_index(short a[16],short b[16])
{
__m256i mask = _mm256_setr_epi16(
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0x0000
);
__m256i x = _mm256_loadu_si256((__m256i*)a[1])
x = _mm256_and_si256(x, mask);
_mm256_storeu_si256((__m256i*)b, x);
}

あとわかるよな?

27 :
ミスった
__m256i x = _mm256_loadu_si256((__m256i*)&a[1]);

28 :
>>__m256i x = _mm256_loadu_si256((__m256i*)a[1])
領域外(a[16])を参照してるような…
ゴミが入るだけだから気にしなくていいってことですか?

29 :
うごいてるっぽいので気にしないことにします。
ありがとうございました。

30 :
ページ境界跨がなきゃ平気

31 :
>>ページ境界跨がなきゃ平気
跨ぐとどうなるんでしょうか。
突然プログラムがあぼーんすることも覚悟しなきゃいけないってことですか?

32 :
アクセス違反例外で停止するわな

33 :
https://software.intel.com/sites/products/documentation/doclib/iss/2013/compiler/cpp-lin/GUID-F4DA723A-DEB9-42D8-82FF-72553EB24273.htm

ここによるとアラインされてなくてもいけるcompiler intrinsicsだわな
アラインが必要なのは

https://software.intel.com/sites/products/documentation/doclib/iss/2013/compiler/cpp-lin/GUID-29CB35FA-F50D-4F6E-88B6-D3A1D83BE311.htm

こっちの方だ
_mm256_load_si256()

ただ当然境界がズレている所は2回ほど読むだろうから速度を気にするのなら
最初から_mm256_load_si256()を使ってデータの配置もアライメント通りにしとくのに限る

配列だと__attribute__((aligned(16)))を付けて置くといいわな

34 :
おっと間違えた

__attribute__((aligned(32)))

35 :
別にアライメントの話じゃないし。
つうか領域外読み込んでも
落ちないからOKとか
それはないから

36 :
上下128ビット跨いだシフトが簡単にできないから
ミスアラインロード使わないとめんどくさいんだよねー

void up_index(short a[16],short b[16])
{
  __m256i x = _mm256_load_si256((__m256i*)a);
  __m128i u = mm_mm256_extracti128_si256(x, 1);
  x = _mm256_alignr_epi8( x, _mm256_castsi128_si256(u), 14);
  _mm256_store_si256((__m256i*)b, x);
}

37 :
 __m128i u = _mm256_extractf128_si256(x, 1);

みすった

38 :
16 x 16 だと結構オーバヘッド覚悟しなきゃいけないみたいですね。
11 x 11 なら128ビットに納まるから上下左右1ビットシフト高速にできますかね?

39 :
ビットボードでも作ってるの?

11ビットとかかえって扱いづらいからやめとき?pslldq/psrldqは8ビット単位だからいろいろ面倒だぞ
16x16でもvextractf128+vpalignrの2命令ですむんんだからそれが一番無難
2のべき乗のほうが扱いやすい

40 :
11 x 11 だと右端と左端がつながっちゃうか。

41 :
>>39
はい、まさにビットボードです。
9 x 9 あれば囲碁9路盤が作れます。
やっぱ2のべき乗ですかね〜。

42 :
SIMDとか並列プログラムは
各要素間の関連が薄けりゃ薄いほど良い
相関ゼロなら100%の性能が出せるが
逆なら劣化する。
要はお前のプランはSIMD向きじゃねえんだよ。
スカラで組めカス。

43 :
なんでよ、ビットボードにSIMD使おうと考えるのは自然だろ。

44 :
上下128ビットの入れ替えが面倒なら、1レジスタで1ボードじゃなくて2レジスタで2ボードみたいな割り当てもアリかな

45 :
19x19は?

46 :
1レジスタに1ボードのらないで2レジスタに2ボードのる状況が想像できない。

47 :
me too

碁盤の19x19なら1列32ビットに割り当てて128ビットの4x5レジスタじゃいかんのか?
256ビットなら3レジスタか。ちと苦しいな

48 :
64ビットごとに19x3という割り当てもありか

49 :
さて、そろそろ団子が実験結果を公開してくれる頃だと思うんだが……
ソース書けた?

50 :
俺囲碁のルールすら知らんが?

51 :
結局SSEとかの機械語でやる以外にC++には高速化の手段がないの?(´・ω・`)
後、変なclassを使わないとか?(´・ω・`)

52 :
クラスで遅くなるとか迷信でしょ

53 :
今はSIMDがあるから
かろうじてC++使ってるけど
他で行けるようになったら
捨てると思う

54 :
クラスを使うから遅くなるんじゃなくて、動的なポリモーフィズムを実現するための仮想関数テーブルや
あるいは過剰にコンストラクタ呼び出しで遅くなる。

55 :
>>53
C++と同程度の速度がでる言語って何よ?

56 :
いやーそんなん無いけどさー
己が納得出来れば良い訳よ
vector4クラスとかあって
最適化コンパイルでSIMD使いますよ〜
で良い。所詮趣味だし俺はそれで乗り換え出来る。
今はfortranやC++以外にはそれすらも無いからな

57 :
過度なオブジェクト指向(笑)やめればC++でもアセンブラに迫る高効率のコード書けますよ

58 :
最近はCとアセンブラを比較することも少なくなったろうな。
数学ライブラリなんかはまだアセンブラで書いてんのかねぇ。

59 :
>>57
おっさんおっさん、それってCとして使うってのとほぼ同じ意味なんじゃ……?

60 :
演算子オーバーロードやテンプレートがCにあるならそうだね
どの程度の抽象化までならオーバーヘッドが生じないかはdvec.hあたりのIntel謹製クラスライブラリ見ればわかるでしょ

61 :
スレ立てるまででもない質問じゃない質問ってどんなの?

62 :
え?そこらへん使ってもアセンブラに迫る高効率のコード吐き出してくれるの?

63 :
コンパイラ舐めんなw

64 :
>>62
C にはない C++ のオーバーヘッドは、例外と仮想関数呼び出しのための関数テーブルアクセスと dynamic_cast<>()。
それらを使わなければ、型チェックの厳しい C として使える。
演算子オーバーロードは変な名前の関数を呼び出してるだけ。
コンストラクタとか(非仮想)デストラクタは C で同じ作法で作ったのと一緒。

最適化で言うと、俺の環境だと this を restrict として扱わないので、
this->func() を func( this ) にするとか人力で最適化されやすくしないといけないことがある。

65 :
>this->func() を func( this ) にするとか

まじで?

66 :
あとはMSVCだとthisポインタをECXで渡すけどSSE*ってECXを暗黙にdestinationとして扱う命令あるじゃん

67 :
みんgwでsse2つかってみたけど全然早くならないorz
何か間違ってんのかな?

68 :
github晒しておくと構いたがりがどこがおかしいのか指摘してくれるよ

69 :
githubってよく知らないんだよなぁ。
イケてるギークはつかってるらしいが。
これを機にやってみるか…

70 :
http://www.age2.tv/rd05/src/up9736.zip.html

githubはとりあえず置いといて、ソースうpします。
物は囲連星というゲームの9路盤のAIです。

makefileが入ってるのでbenchmark.exeをmakeしてみてください。
うちのPC(core i7 4790)で40秒くらいで終わるベンチマークができます。
ベンチの内容はAIが3手プレイします。

SSE2で最適化したいクラスはPointSetというクラスで、
128bitつかって 10 x 10 のビットボードを実装しています。

SSE2化してみましたが速くなりませんでした。

スレの皆さん最適化よろしくお願いします。

71 :
SSEって最適化できてもせいぜい20%アップ程度の気がするな。
アルゴリズムの見直しとか、CPU演算以外とかはやりきってるか?

72 :
ちなみにmakefileは依存関係全然考慮してないので毎回クリーンしてくださいw
コンパイラは64bit MinGWです。

73 :
オッとリロードしてなかった。
>>71
アルゴリズム見直しで速くなるのが一番いいんですけどね〜
なかなかアイディアが湧かないのでとりあえず手っ取り早いSSEを試してみたいです。

74 :
>>73
i7 4790ならAVX2やiGPGPUで速くしたほうがいいんじゃないのか?
データ並列に適していないのにSIMDにしてもたいして速くならないと思うけど
いまの適している?

75 :
128bitで一つのビットボードになっていてもろandとかorとかandnotとかさせるので
SSE2で速くなるかなと思ったんですが。
AVX2は256bitですよね?どうやって適応させるか難しいです。
iGPGPUは使い方しらないorz

76 :
iGPGPUとか全然ダメ。
並列処理向きな処理でも激遅。
最低でもメモリ空間共有がサポートされなきゃゴミ。

77 :
>>70はピンポイントでSSE化したい所、出来そうな所だけピックアップしてくれよ。
これでは解析が手間。

78 :
ボトルネックがピンポイントで分かってないからこそ遅いのでは?

79 :
>>77
SSE化できそうなところはPointSetというクラスですが、
じつはもうSSE化してます。
でも速くなんなかった。

なぜ速くならなかったか教えていただけると助かります。

>>78
PointSetボトルネックになってないんですかね。
結構使ってるはずなんですが。

80 :
関数ごとのプロファイルとってみて総実行時間を見てみるといいよ
並列化できない(と思ってる)ところが最大のボトルネックになってるなら他をいじってもどうしようもない

81 :
最適化ってのはボトルネックになってるところ測定してからやるもんだぞ

82 :
なんか性能測りたい関数がインライン展開されちゃって測れないっぽいT△T

83 :
だから計測しろって

84 :
ビットフィールドの立ってるビット数える関数をSSE4.2のpopcnt使ったらえらい速くなったんだが?

85 :
ちょっと出かけます。
夕方には帰ってくる。

86 :
気を付けてな

87 :
ちょっと伺いたいんですがavxまでで8bit(1byte)の中で上半分、下半分を取り出すのはありますか?
avx2までいくとpextというmaskをとった部分のビットだけ抽出するのはあるようなんですが・・・

88 :
pextというとBMI2かね
汎用レジスタでの話ならシフトとandじゃダメな理由があるのかね
連続したnビットを取り出したいだけならレイテンシの長い(3clk)pextを使う価値があまりない

mov eax, 0fh
mov edx, f0h
pext eax, esi, eax
pext edx, esi, edx
5クロック

mov eax, esi
mov edx, esi
shr edx, 4
and eax, 0fh
and edx, 0fh
3クロック、movが消えるなら2クロック

89 :
>>88
なるほど!ありがとうございます

90 :
どういう並びにしたいの?
こういう意味?
http://stackoverflow.com/questions/12795462/how-do-i-extract-32-x-4-bit-from-16-x-8-bit-m128i-value

91 :
>>90
ちょうどこんな感じです!
このソースみたいにlowerとupperを取り出して返す関数つくろうと思ってました

下4bitはそのまま0xfでmaskして、上4bitは4bitシフトしてからmaskなのでこのコードで行けそうです
ありがとうございます

92 :
え?

movzx eax, al
shr eax, 4

じゃいかんのか?

93 :
どうしてもSIMD使いたいんだとさ
クロック数がかえって増えようとも

94 :
いえアセンブリがわからないだけです!
>>92でいけるのならやってみます!ありがとうございます!

95 :
gprofの結果ビットボードを座標のベクタに変換する関数PointSet::toVector()が時間かかってることが分かった。
すまん、ちょっと飯食ってくる。

96 :
大概は思わぬところでサイクルを食ってるもんだ。
食うのは飯だけにしたいものだな。

97 :
PointSet::toVector()は一回最適化試みた箇所なんだよなぁ
正直これ以上アイディアうかばん。

98 :
なにをしてるか知らないがここが時間食ってる。
8ビットや16ビットで区切って計算結果を配列に入れといて利用するとかできないか?


int size()const
{
int res=0;
unsigned long long i;
i = u.table[0];
while (i)
{
++res;
i = (i - 1)&i;
}
i = u.table[1];
while (i)
{
++res;
i = (i - 1)&i;
}
return res;
}

99 :
あとここも。
32bitでの実行だが。


int get(Point p)const
{
// return !((*this) & x_line[p.x()] & y_line[p.y()]).empty();
return (table[p.y() & 1] >> (p.x() + (p.y() >> 1) * 10)) & 1;
}

100 :
立ってるビット数が多いときはその数え方だと時間喰いそう


100〜のスレッドの続きを読む
ネットワークプログラミング雑談
C++によるDICOMファイル解析
この先き主流となる言語
統計解析R たぶんpart3くらい
Windows 10 UWPアプリ開発 Part 2
C++によるDICOMファイル解析
構造化プログラミングはまだ必要ではないのか?
人工知能ディープラーニング機械学習の数学 ★2
自然言語処理スレッド その5
結局開発で最も大切なのはテーブルの正規化と制約
--------------------
明日の日経平均をなるべく予想するスレッド〜0001〜
【mobage】グランブルーファンタジー質問スレ174
Russell Hobbsスレ
【アリスギア】アリス・ギア・アイギス Part790
「武漢コロナウイルスは空気感染どころか電波感染する。岩田記者会見は危険。ダメ絶対」 内閣府が発表 [422186189]
◎ ブラザー製品使用者集合 part12 ◎
【HKT48】荒巻美咲応援スレ★15【みるん】
〓〓 Liverpool FC 〓 985 〓〓
アリアンロッドRPG CL164
【ゴブリンスレイヤー】蝸牛くも 28匹目【ゴブスレ】
ナチュラリープラスを熱く語ろう【全心全進】
さぁて今号の花とゆめ・ザ花とゆめ★223号
かわいいけど不潔そう・臭そうなアニメキャラ
【名演奏】諸石幸生【ライブラリー】
【個人経営】居酒屋オーナー調理人専用スレッド
ワンダープロジェクトJシリーズを語りつくせ! 9
さくら学院★1755時限目
東急田園都市線はグルメ空白地帯?
スレッド立てる程でもない質問・愚痴・雑談など@既婚男性141
■■速報@ゲーハー板 ver.52084■■
TOP カテ一覧 スレ一覧 100〜終まで 2ch元 削除依頼