1 名前:デフォルトの名無しさん mailto:sage [2005/10/27(木) 02:55:36 ] C++やインラインアセンブラ、SSEなどによる高速化の手法 について語りましょう。
596 名前:デフォルトの名無しさん mailto:sage [2011/05/30(月) 10:20:14.97 ] 結論から言うとメモリの転送がボトルネックです。 書き出しのアライメントを揃える事とstreamを使う事で何割かは改善出来ますが、基本的に速く出来ません。 最適化とは遅い部分を探し出す事に他なりません。 安直にSSEとかマルチプロセッサにしようと思わず、真にボトルネックを見つけられるようになりましょう。 真に遅い部分が分かったなら、平均化フィルタと何が違うのか、どうしてもう速く出来ないのかが理解出来るようになります。
597 名前:デフォルトの名無しさん mailto:sage [2011/05/30(月) 11:55:38.13 ] つまり, メモリがボトルネックになっている以上, いくら演算速度をあげても効果は薄いということですね。 高速化に対するアプローチも教えていただき, 大変勉強になりました。 速度に影響する要因をまだ一部しかわかっていない私には難しいかもしれませんが、これから知識を得ていきたいと思います。 御回答ありがとうございました。
598 名前:デフォルトの名無しさん mailto:sage [2011/06/13(月) 15:11:06.75 ] >>596 メモリの転送がボトルネックなのか、演算部分がボトルネックなのかは どうやって判断すればいいのですか?つまりどこを見たらよいのか。 あるいはあなたはどうやってますか? ツールとか使うのでしょうか?
599 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 01:59:27.76 ] メモリに対する操作や演算の大半は、ツールを使って調べるまでもなく、単純で十分に短い。 なので、ソースコードを眺めれば、そこがSIMDを使う事で高速化すべきか否かは、すぐに見分けが付く。 演算そのものが単純で短い場合は、複数種の演算を1命令にまとめることが出来るかどうかで判断していい。
600 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 03:12:38.27 ] >>598 理論値と実際の処理時間の差で見積もる。 また、CPU メーカのプロファイラを使えば、命令のリタイア数とか、キャッシュミスなどのイベントの数を計測できる。
601 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 03:21:34.39 ] Cのソース眺めるだけじゃわからんだろうに、汗で判断できるぐらいにならんと
602 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 03:46:45.40 ] 最適化もしない状態のことだったりして
603 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 07:12:37.77 ] CPUのつもりになってメモリアクセスパターンを想像したら見えてくるかもしれんね
604 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 12:49:22.23 ] >>600 理論値ってアセンブラニーモニックを命令毎にスループットやレイテンシの累計計算して 全体で何クロックかかるかを算出した値という意味ですか? それで実際にどれだけクロックかかるかとの比較をする、と。
605 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 15:49:14.35 ] メモリ参照をいかに少なくするかでしょ、今時の高速化は
606 名前:デフォルトの名無しさん mailto:sage [2011/06/14(火) 20:56:50.75 ] >>598 すまんレス遅れた。 メモリのアクセス回数と演算回数は見積もれるな? c[i] += d[i] * e だったらeはレジスタに乗っているものとして無視して、 読み込み2回(c, d)、書き込み1回(d)、加算1回(+=)、積算1回だ。 回数を見積もったら、加減積算はそのまま、メモリアクセスと除算とsqrtは10倍、その他sin/pow/log/expとかは100倍するんだ。 比率はいい加減なので、数倍しか違わないなら全体的に最適化。 その中で、演算が効いてそうならSIMD化だ。 あとは、何よりも実測が重要。
607 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 00:38:17.15 ] >>606 ありがとうございます ソースの式から各演算毎にウエイトを用いてかかるコストを見積もるって意味だったんですね。 それでメモリアクセスより演算のコストの方が高そうであればSIMD化すると。 例の c[i]+=d[i]*eの場合 メモリアクセスコスト 3*10=30 演算コスト 1+(1*10)=11 なのでメモリアクセスのほうがコストが高いから、SIMD化しても早くならないだろうと考えるわけですね。 >書き込み1回(d) 揚げ足とるつもりではないのですが これは(c)への書き込みの間違いですよね?
608 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 00:44:29.40 ] SSEで例えると、メモリの読み書きはSSEでやったほうが断然高速だから そんなコストとか馬鹿な計算する以前に速くなることは確定している。
609 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 00:57:58.94 ] そんな前時代的などんぶり計算しても意味ないよ プロファイラにかけな
610 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 01:38:06.85 ] キャッシュが考慮されてないよね
611 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 01:49:45.83 ] > 間違いですよね? その通り。 >>608 総和とか本当にメモリがボトルネックだとスカラでやっても大して変わらない。 >>609 そう、机上は本当に大ざっぱな見積もりだけで基本的には実測が重要。 でもVTuneなんかは他にも項目が多過ぎて目星付けるのも勘がいるんだよ。
612 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 16:31:35.18 ] SSE使わんと絶対にプリフェッチされない、とかだったら正しかった。
613 名前:デフォルトの名無しさん mailto:sage [2011/06/15(水) 21:26:27.67 ] >>610 畳み込みとの違いのヒントを示し忘れてた。 同じアドレスを複数回読むなら、最初の1回を見積もりに入れておけばいい。 勿論キャッシュに入らないサイズだと全てカウントする必要がある。
614 名前: ◆0uxK91AxII mailto:sage [2011/06/16(木) 15:08:44.21 ] メモリ周りがボトルネックなら、prefetch*とmovnt*。 とりあえず、CPUによっては32[bit]のmovntiは遅いから使わない方向で。 実行環境が単一でない場合、大雑把な見積もりで十分だと思ってみる。
615 名前:デフォルトの名無しさん mailto:sage [2011/07/02(土) 13:07:18.11 ] VCの__declspec(align(16))や#pragma pack(push, 16)って 一時オブジェクトには効かないんでしょーか? std::map、std::pair周りをソースひっぱってきて、__m128を含んだクラスを 受け取れるようにしたんですが、 aligned_stl::pair<int, hoge> pairArg = aligned_stl::make_pair(1, Hoge); mapFuga.insert(pairArg); とやるといけるけど mapFuga.insert(aligned_stl::make_pair(1, Hoge)); とすると、一時オブジェクトがアラインされてなくてこけます・・・・ 何か抜け道ないですかね(´・ω:;.:...
616 名前:デフォルトの名無しさん mailto:sage [2011/07/02(土) 13:20:21.30 ] 間違えたorz ”make_pairの返す一時オブジェクトが”アウトですた。 make_pair(1, Hoge); ←×(落ちる。) aligned_stl::pairArg(1, Hoge); ←○(落ちない。) なのでコンストラクタで直接渡せばいけるけども。 Hogeにもpairにもアライン指定つけてるんだけどなぁ・・・
617 名前:デフォルトの名無しさん mailto:sage [2011/07/02(土) 13:22:04.01 ] また間違えた・・・・連投すみませんorz 誤:aligned_stl::pairArg(1, Hoge); ←○(落ちない。) 正:aligned_stl::pair<int, hoge>(1, Hoge); ←○(落ちない。)
618 名前:デフォルトの名無しさん mailto:sage [2011/07/02(土) 22:44:49.03 ] mapの方がアライメントされてないんじゃない?
619 名前:デフォルトの名無しさん mailto:sage [2011/07/02(土) 23:38:54.68 ] std::pair<int, hoge>からaligned_stl::pair<int, hoge>へのコードがバグってるんじゃないの
620 名前:618 mailto:sage [2011/07/03(日) 00:21:26.44 ] 適当な事書いたと思ったけど、std::mapにカスタムアロケータでいけるっぽい ttp://ideone.com/sOWl6
621 名前:デフォルトの名無しさん mailto:sage [2011/07/03(日) 02:45:36.94 ] >>618 mapの方は、__declspec(align(16))みたいなことはしてませんが カスタムアロケータを渡しており、カスタムのpair(aligned_stl::pair)を使うように指定してます。 >>619 わかりづらくてすみませんorz std::pairは一切使っておらず、aligned_stl::pairのみです。 make_pairもaligned_stl::pairを返します。 >>620 ありがとうございます、Win7 64bitのVC2008Expressで動かしてみましたが カスタムアロケータのc.insertで、Hoge()の引数なしのコンストラクタで落ちます(x_ = static_x) Hoge hoge; c.insert( ::std::make_pair<int,Hoge>(i, hoge) ); とやると、make_pairのところのコピーコンストラクタで落ちます( x_ = src.x_)。 なので、前回書いたのと同様に Hoge hoge; std::pair<int, Hoge> pairArg(i, hoge); とやってからpairArgを渡すと落ちませんでした(c.insertのみ。d.insertは落ちる)。 どうもpairに関してはアラインメント指定しなくても、中のメンバが アラインメント指定されていれば問題ないようですね(アロケータだけでよい?)。 もうちょっと試してまた報告します。 ただ、2008だとアラインメント指定が一時オブジェクトに対して効かないと考えたほうが いいのかもしれませんね(´・ω:;.:... (インライン展開された場合は除く) ありがとうございました。
622 名前:621 mailto:sage [2011/07/03(日) 03:25:25.25 ] あと、x64をターゲットにすれば落ちませんでした(デフォルトのアラインメントが16バイトだから?) ついでに質問なのですが、>>620 のchar unused1__とunused2__はどういう意味でしょうか?
623 名前:デフォルトの名無しさん mailto:sage [2011/07/03(日) 03:47:36.78 ] それって構造体のメンバのアライメント指定であって、アドレスのアライメント指定する機能って基本的になくね。 x64だと関数の始まりのスタックは16バイトアライメントになるようになってるから、問題発生しにくいとか。 というか、落ちる落ちない以前にデバッガで逆アセ見れば、どういうコードが生成されているかすぐわかると思うんだが。
624 名前:デフォルトの名無しさん mailto:sage [2011/07/03(日) 04:52:17.82 ] >構造体のメンバのアライメント指定であって あああ、なるほど・・・x64なら安全、と考えるのは危険ですね。 >逆アセ見れば 見てます、きっちりmovaps使ってますw で、ウォッチ窓で見ると一時オブジェクト側のアドレス下位1バイトが0でないので 明らかにアラインされてないのが問題です。 __m128を使うだけなら、構造体の代入演算子やコピーコンストラクタで _mm_loadu_psとか使えばいいんでしょうけど。 誤解されるような書き方だったかもしれません、決して手詰まりというわけではないです。 どうにかしてアラインメント保ったままmapに突っ込めないかなー、というだけでした。
625 名前:618 mailto:sage [2011/07/03(日) 11:13:12.43 ] ダメ、だったか・・・ (Initial D 星野好造風に) 2010だからこっちでは本来の動作確認はできないけど、コレならどうだろう ttp://ideone.com/h4QFh
626 名前:621 mailto:sage [2011/07/03(日) 15:51:09.00 ] >星野好造風に 渋すぎるwww ありがとうございます、試して無事動きました(#if 1でも0でも) 予測しておられた?通り、char unused1__が含まれていようと問題ないんですね。 構造体の型自体は(__m128のような16byte境界を要求するものが含まれていれば) 先頭アドレスが16byte境界でさえあればいいような配置になっているけど、 2008は関数の返す一時オブジェクトのアドレス調整が抜けている、ということかな・・・ 大変参考になりました、ありがとうございました。
627 名前:デフォルトの名無しさん mailto:sage [2011/07/03(日) 22:56:56.22 ] int i = 10; int * p = &i;//int型ポインタpにiのアドレスを代入する 簡単。 char str1[] = "abcde"; char * str2 = "abcde"; 上と下は同じでどっちを使ってもいい。
628 名前:デフォルトの名無しさん mailto:sage [2011/07/03(日) 22:59:55.40 ] sizeof(str1) != sizeof(str2)
629 名前: ◆0uxK91AxII mailto:sage [2011/07/04(月) 00:40:54.13 ] str1++;
630 名前:sage [2011/07/05(火) 07:42:33.24 ] sizeof とか return とかにかならずカッコをつける人がいるけど何で?
631 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 08:17:06.42 ] sizeofとdefinedは関数として意味が通ってるから括弧付けちゃうな。 (a || b) && (c || d) も括弧いらないけど、あった方が読みやすいのと似てる。 returnに括弧付ける人の気持ちは聞いた事が無いから分からない。
632 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 08:18:59.63 ] 間違えた(a && b) || (c && d)だ。
633 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 08:43:27.32 ] >>630 sizeofの中身によって括弧つけたりはずしたりしてるのかい? おれはそんなとこ気を使っても意味ないと思うので常につけてる。 sizeofの括弧省略してるコードってほとんど見たこと無いしな。
634 名前: ◆0uxK91AxII mailto:sage [2011/07/05(火) 14:20:59.67 ] >>630 struct s{int i;}; sizeof struct s; :b
635 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 19:23:54.89 ] 20年以上C書いてるけど、sizeof のカッコが省略可能なんて知らなかった…実際そんな記述を見たことがなかった。 return にはカッコつけない。
636 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 19:33:01.76 ] return (0); とかは見たことあるけど、sizeof int は見たことないからなぁ。
637 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 19:36:15.27 ] それは違反です
638 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 22:56:13.54 ] 余分なカッコが多すぎるコードってみにくくて嫌いだ。 if (a == 0 && b == 0 || c == 0 && d == 0) if ((((a == 0) && (b == 0)) || ((c == 0) && (d == 0)))) この二つだと上の方がはるかに見やすいと個人的には思うが、 見やすさを優先してカッコをつけるとか言って下のように書く人がいる。
639 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 23:13:08.81 ] LISPに慣れているせいか、かっこをつけておくと 式の構造が立体的に(ネストが深いほど浮き上がって)見えるんだ
640 名前:デフォルトの名無しさん mailto:sage [2011/07/05(火) 23:16:36.97 ] 確かに人間が見ると見やすいかも知らないが、 人間以外(コードアナライザの類とか)には 見やすくないと判断されるんで仕方がなくつけてる。
641 名前:デフォルトの名無しさん mailto:sage [2011/07/06(水) 00:43:52.44 ] >>638 if ( ((a == 0) and (b == 0)) or ((c == 0) and (d == 0))) って書く。論理演算はキーワード使う方がビット演算との取り違えも防げる。
642 名前:デフォルトの名無しさん mailto:sage [2011/07/06(水) 00:54:25.86 ] AndAlsoですね、わかります
643 名前:デフォルトの名無しさん mailto:sage [2011/08/01(月) 22:47:25.26 ] >>638 何処からが"余分"なのかを巡って紛糾する事も多し。
644 名前: ◆0uxK91AxII mailto:sage [2011/08/02(火) 00:52:38.09 ] #define EQUAL(a, b) (a==b) #define OR(a, b) (a||b) #define AND(a, b) (a&&b) if (OR(AND(EQUAL(a, 0), EQUAL(b, 0)), AND(EQUAL(c, 0), EQUAL(d, 0)))) 見づらいって言うか、吐き気がするほど読みづらい。
645 名前:デフォルトの名無しさん mailto:sage [2011/08/02(火) 08:50:56.65 ] こんな手法でちまちま高速化したところで、もっと簡単なGPGPUの並列処理に 圧倒されるだけ。
646 名前:デフォルトの名無しさん mailto:sage [2011/08/02(火) 19:46:47.70 ] GPGPUが簡単? OpenCLの厄介さを身を以て知って濃い。
647 名前:デフォルトの名無しさん mailto:sage [2011/08/02(火) 22:19:04.98 ] if ((!a && !b) || (!c && !d))
648 名前:デフォルトの名無しさん mailto:sage [2011/08/02(火) 22:20:48.41 ] 括弧が
649 名前:デフォルトの名無しさん [2011/10/10(月) 19:26:30.72 ] 指定桁数で四捨五入する以下の関数の実行速度を上げたいの。 (valueは0〜9999、digitsは0〜5が保証される) SSE使って高速化頼む。 double NormalizeDouble(double value, int digits) { static double t0[] = { 1, 10, 100, 1000, 10000, 100000 }; static double t1[] = { 1, 0.1, 0.01, 0.001, 0.0001, 0.00001 }; return (int)(value * t0[digits] + 0.5) * t1[digits]; }
650 名前:デフォルトの名無しさん mailto:sage [2011/10/10(月) 19:27:11.99 ] ベクトル化は不要よ。
651 名前:デフォルトの名無しさん mailto:sage [2011/10/10(月) 19:39:28.17 ] あら、なんかデジャブ
652 名前:デフォルトの名無しさん mailto:sage [2011/10/10(月) 19:49:57.12 ] >>649 最適化スレで終了宣言してからにしろよ
653 名前:デフォルトの名無しさん mailto:sage [2011/10/10(月) 19:50:50.26 ] でもベクトル化で少しは早くなるわよ
654 名前:デフォルトの名無しさん mailto:sage [2011/10/10(月) 19:56:01.06 ] >>652 あっちはCでだから。 >>653 使う方がスカラーなのよ。
655 名前:デフォルトの名無しさん [2011/10/11(火) 20:37:34.29 ] >>2 これっていまでのそうなの?
656 名前:デフォルトの名無しさん mailto:sage [2011/10/11(火) 21:19:20.86 ] >>655 お前はQuickCでも使ってろよ
657 名前:デフォルトの名無しさん mailto:sage [2011/10/13(木) 04:16:06.44 ] なんで PSLLB だけないんだろう?
658 名前:デフォルトの名無しさん [2011/11/05(土) 00:09:14.57 ] x64の組み込み命令で、xmmレジスタに定数を代入したいんですが __m128i xmm0; xmm0.m128i_u64[0] = 0x1212121212121212; xmm0.m128i_u64[1] = 0x1212121212121212; これより速い方法はありますか? 各バイトが必ず同じ数字になるとして
659 名前: ◆0uxK91AxII mailto:sage [2011/11/05(土) 00:32:10.02 ] >>658 定数はループの外で置きっぱなしにするし、x64だとレジスタが多いから、そのままで良いと思ってみる。 mov eax, 12121212H movd xmm0, eax pshufd xmm0, xmm0, 000H
660 名前:デフォルトの名無しさん mailto:sage [2011/11/05(土) 08:34:55.68 ] VC++だと __m128i xmm0 = {0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12, 0x12}; でmovdqaを使うようになる
661 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/11/11(金) 03:05:16.98 ] VC++だとそれでいいけどGCCだとコンパイル通らなかったりするんだよね。 最適化ルーチンが腐ってなければ大体これでいけるけど。 __m128i xmm0 = _mm_set1_epi8(0x12); というかアセンブリ出力読むと良いよ。
662 名前:デフォルトの名無しさん mailto:sage [2011/11/28(月) 22:54:57.76 ] >>654 お前はスクルトでもツカッテロ
663 名前:デフォルトの名無しさん mailto:sage [2011/11/28(月) 23:12:10.08 ] スクカジャの方が良いよ
664 名前:デフォルトの名無しさん mailto:sage [2011/12/17(土) 22:41:12.80 ] signed shortの配列に floatもしくはdouble型の乗算をして クリップ処理をほどこし signed shortの配列に戻すのを SSEにしたいのでやってください これ signed short s[100]; float f init a; for (a=0;a<100;a++) s = s[a] * f;
665 名前:デフォルトの名無しさん mailto:sage [2011/12/17(土) 23:06:34.33 ] それ、どう考えても実数型⇔整数型のコストがでかすぎる。 たった100件でいいなら実数で持てないの?
666 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 00:38:02.93 ] >>665 100は例で実際はは数千万程度 waveファイルPCMの加工で
667 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 01:11:33.16 ] >>664 自分で学習しないと答えを聞いても使えんよ。 そもそも意味不明なコード書かれても誰も答えられない。
668 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 01:24:45.87 ] >>667 ワカンネーならクチダスな
669 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 01:28:37.19 ] #1:すぐに話の風呂敷を広げる #2:よく理解しないまま突っ走る #3:嫌われていると勝手に思い込んでいる #4:キレて他人に八つ当たりする #5:やたらと自分の正当性を主張する #6:何も自分で決めない #7:何かにつけて理屈をこねる #8:やたら褒めてほしがる #9:他人の話を聞かない #10:何が言いたいのかわからない japan.zdnet.com/sp/feature/07tenthings/story/0,3800082984,20421237-2,00.htm
670 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 01:29:57.57 ] 自分語り 乙w
671 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 03:08:00.84 ] s = s[a] * f; って何がしたいんだw
672 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 04:36:52.55 ] 話の内容を察するに、s[a] *= f; ってことだとおもうけれど
673 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 15:35:15.11 ] それじゃ飽和処理してないし
674 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 15:54:06.36 ] つーか、コンパイル通るの?
675 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 17:04:56.31 ] 何も書いて無いけどどうせ色々特殊処理もあるんだろうから、予め 0x0000-0xffffに対する結果を128K byteのテーブルに計算しておき、 後はテーブルルックアップすれば良いだろw
676 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 18:08:27.37 ] いっそ0x0000-0xffffffffに対する結果を16GByteのテーブルに(ry
677 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 18:58:44.22 ] >>666 音を大きくしたり小さくしたりしたいだけなら、0.0〜1.0を、0〜255 とか 0〜65535 とかにして、 整数演算に帰結させてMMXのほうが速いんじゃ…
678 名前:デフォルトの名無しさん mailto:sage [2011/12/18(日) 23:16:56.16 ] お前らやさしいな
679 名前:デフォルトの名無しさん mailto:sage [2011/12/19(月) 02:15:44.43 ] 初めてSSEに触れるので、まずは簡単なコードを作成してみたのですが、 SSEを使わないほうが40倍も速いという驚愕の結果が出ました。 何が間違っているんでしょうか?? コンパイラ:VC++2005(Releaseモード、浮動小数点モデル:FAST) float* f4pakAdd( float* pfA, float* pfB ) { _declspec( align( 16 ) ) static float fC[ 4 ]; _asm { mov ebx, pfA movaps xmm0, oword ptr [ebx] mov ebx, pfB movaps xmm1, oword ptr [ebx] addps xmm0, xmm1 movaps fC, xmm0 } return fC; } 呼び出し側 for( int i = 0; i < 10000000; i++ ) { pfC = f4pakAdd( fA, fB ); }
680 名前:679 mailto:sage [2011/12/19(月) 02:15:55.13 ] _asmのスコープをコメントアウトして、以下のようにスカラの加算を並べたほうが40倍速くなりました。 fC[ 0 ] = pfA[ 0 ] + pfB[ 0 ]; fC[ 1 ] = pfA[ 1 ] + pfB[ 1 ]; fC[ 2 ] = pfA[ 2 ] + pfB[ 2 ]; fC[ 3 ] = pfA[ 3 ] + pfB[ 3 ];
681 名前:679 mailto:sage [2011/12/19(月) 02:17:15.89 ] fA、fBは以下のように宣言しています。 _declspec( align( 16 ) ) float fA[ 4 ] = { 1.0f, 2.0f, 3.0f, 4.0f }; _declspec( align( 16 ) ) float fB[ 4 ] = { 5.0f, 6.0f, 7.0f, 8.0f };
682 名前:デフォルトの名無しさん mailto:sage [2011/12/19(月) 02:34:54.14 ] パイプライン化したコードでもないし、スタックチェックが行われている気がするが。 逆アセ確認したかい?
683 名前:デフォルトの名無しさん mailto:sage [2011/12/19(月) 02:52:57.82 ] 40倍も遅いというのはインライン展開されずに普通に関数呼び出しされちゃってるんじゃないの
684 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/12/19(月) 02:58:55.10 ] インラインアセンブリは関数のインライン展開を阻害するから どうしてもASMを使いたいとか宗教的な理由が無い限りintrinsicsを使うべき 強いて関数呼び出しにするなら__fastcallにするかな。
685 名前:679 mailto:sage [2011/12/19(月) 02:59:52.23 ] >>682 逆アセしてみました。 ■SSE使用 mov ebx, pfA 004115DE mov ebx,dword ptr [pfA] movaps xmm0, oword ptr [ebx] 004115E1 movaps xmm0,xmmword ptr [ebx] mov ebx, pfB 004115E4 mov ebx,dword ptr [pfB] movaps xmm1, oword ptr [ebx] 004115E7 movaps xmm1,xmmword ptr [ebx] addps xmm0, xmm1 004115EA addps xmm0,xmm1 movaps fC, xmm0 004115ED movaps xmmword ptr [fC (417540h)],xmm0 ■SSE未使用 fC[ 0 ] = pfA[ 0 ] + pfB[ 0 ]; 004115DE mov eax,dword ptr [pfA] 004115E1 fld dword ptr [eax] 004115E3 mov ecx,dword ptr [pfB] 004115E6 fadd dword ptr [ecx] 004115E8 fstp dword ptr [fC (417540h)] 配列インデックス1以降も同じ感じです。 どちらもまじめに演算しているだけのようですね・・・
686 名前:679 mailto:sage [2011/12/19(月) 03:16:03.78 ] すみません、>>685 はDebugモードでの逆アセで、Releaseモードでは以下のように インライン展開されていました。 先ほどのはどちらも関数呼び出しを行っていました。(お決まりのpushやret) ■SSE使用 for( int i = 0; i < 10000000; i++ ) { pfC = f4pakAdd( fA, fB ); 004011A6 lea ecx,[esp+10h] 004011AA lea edx,[esp+30h] 004011AE mov dword ptr [esp+28h],ecx 004011B2 mov dword ptr [esp+2Ch],edx 004011B6 mov eax,989680h 004011BB jmp WinMain+190h (4011C0h) 004011BD lea ecx,[ecx] 004011C0 mov ebx,dword ptr [esp+2Ch] 004011C4 movaps xmm0,xmmword ptr [ebx] 004011C7 mov ebx,dword ptr [esp+28h] 004011CB movaps xmm1,xmmword ptr [ebx] 004011CE addps xmm0,xmm1 004011D1 movaps xmmword ptr [__fmode+10h (403380h)],xmm0 004011D8 sub eax,1 004011DB jne WinMain+190h (4011C0h) }
687 名前:679 mailto:sage [2011/12/19(月) 03:17:00.05 ] ■SSE未使用 for( int i = 0; i < 10000000; i++ ) { pfC = f4pakAdd( fA, fB ); 0040114A fld dword ptr [__real@40c00000 (40213Ch)] } これって、定数演算だから、事前に計算した結果をメモリに置いておいて、 ロードするだけってオチでしょうか・・・(汗ンブラ)
688 名前:デフォルトの名無しさん mailto:sage [2011/12/19(月) 03:18:45.19 ] SSEなんてたいして効果無いんだろ。 コンパイラまかせでいい。 小手先のテクより、アルゴリズムやGPU使用など劇的変化が見込める所を研究すべき。 SSEは最高が20%とかだろ。
689 名前:679 mailto:sage [2011/12/19(月) 03:19:44.86 ] >>683 Debugモードではいずれも関数呼び出しありで、 Releaseモードではいずれもインライン展開されていました。 両モードでこういった差があることは意識していなかったので勉強になりました。 >>684 団子さん、ありがとうございます。 今回は勉強の一環としてアセンブラを使いましたが、 できるだけ組み込み関数を使うことにします。 (__fastcallにしたら速度が半分に低下してしまいました・・・)
690 名前:デフォルトの名無しさん mailto:sage [2011/12/19(月) 03:31:49.83 ] 実用上はインライン展開で速くなることは稀。 関数にするからには一カ所より多くの所からコールされる可能性が高く、 インラインにしなければCPUキャッシュに乗っている場合に関数を使い回しができて高速化される。
691 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/12/19(月) 03:36:24.22 ] 変数をスタックに積むのって結構無駄だろ? 呼び出し元の関数が小さい場合は特にね。 VC++の32ビット版の場合、__fastcall規約だとxmmレジスタで3つまで渡せるよ。
692 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/12/19(月) 22:10:00.23 ] 失礼 VC++だと__m128{,i,d}は呼び出し規約にかかわらずレジスタ渡しになるね てかレジスタ渡し以外不可能。 ちなみにgcc(Linuxなど)とかだといったんストアしてポインタ渡しになる。
693 名前:デフォルトの名無しさん mailto:sage [2011/12/20(火) 02:40:46.25 ] >>692 なんか昔より太ってね?
694 名前:デフォルトの名無しさん mailto:sage [2011/12/20(火) 02:51:35.79 ] なりすましとか勘弁してください
695 名前:デフォルトの名無しさん mailto:sage [2011/12/22(木) 08:12:33.37 ] >>690 今の時代のインライン展開って、call命令減らすというより、コンパイラの最適化効きやすくするためというほうが大きいんじゃない? うまくいけば数命令ぐらい減るし。
696 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/12/22(木) 22:22:40.93 ] ステップ数の少ない小さい関数限定だな
697 名前:デフォルトの名無しさん mailto:sage [2011/12/22(木) 23:47:29.24 ] C++の話なら、たんなる要求に過ぎないので、 ステップ数が大きければコンパイラがinline指定を無視するような気もするな。
698 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 01:18:10.46 ] gettimeofday()してusecを返すだけの関数はインライン展開したけれど、 (sec%1000)*1000*1000を足したら関数呼び出しになった(gcc4)。
699 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 01:49:52.05 ] >>698 1関数呼び出しのみの関数はエイリアスとみなしてるんじゃね? 後者は引数渡しのオーバヘッドが無いし、インライン展開のメリットがあるか微妙な線だな。
700 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/12/23(金) 01:52:20.02 ] おいらC++の場合だとそれほど大きくないクラスならヘッダ内に関数実装も含めて書いちゃうし 最適化時にどれがどう展開されるかはコンパイラ任せという罠。
701 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 04:20:10.94 ] インラインやマクロは最初から使わない方が良い。 EXEでかくなり、メモリ使用量も増える。 自分はオプションでインライン展開しない設定にしてる。 それで実測してみて、関数コールが実際に負荷になっていれば書き換えれば良い。 しかしそんなことが有ったことがない。 速いという思い込みでインライン指定してるだけとおもう。
702 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 06:03:00.08 ] templateなんか駆使すると インライン展開されないと 桁違いに遅くなるけど
703 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 09:45:11.17 ] maxとかabsとかSSE一命令でいけるのにわざわざレジスタ退避してcallしてたら コードサイズもでかくなるしいいことないだろ 関数のサイズをちょっとずつ大きくしてくとわかるけど インライン展開の閾値を超えた瞬間にインラインで展開されてた数命令規模の関数が 一気にcallに置き換えられてサイズが爆発するから
704 名前: ◆0uxK91AxII mailto:sage [2011/12/23(金) 09:52:16.15 ] mapとか、コンテナにvoid *を突っ込む癖が付いてしまう。
705 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 09:54:07.89 ] C++は面罵関数がインライン展開されなかったら話にならないからな。 自分はオプションでインライン展開を積極的に行なう設定にしている。 それで実測してみて、EXEの大きさやメモリ使用量が実際に問題になっていれば書き換えればいい。 しかしそんなことがあったことがない。 増えると言う思い込みでインライン設定を毛嫌いしているだけだと思う。 実際、ベクタ化の方がよっぽどコードサイズが増えるよ。
706 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 09:55:04.79 ] あと、SSEの場合アラインメントの推論が切れるのがかなり問題で 関数単位でコンパイラが見てしまうと ポインタにはアラインメントの情報が乗っていないから iccなんかは気を利かせて一旦アラインメントされたスタック上にコピーする コードを挿入してしまったりする アラインメントされてないとペナルティが大きいからね インライン展開されると アラインメントされているスタック上の変数や 明示的な_mm_load_psなんかから 変数やポインタのアラインメントを推論するので アラインメント前提のコードを生成させることができる
707 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 11:26:47.88 ] どんな変数も問答無用でアライメント16にするようにコンパイルする設定があれば 面倒な記述を減らせると思うんですが、何かデメリットあるんでしょうか? メモリの隙間ができて勿体無いとかあるかもしれませんが、メモリ量の多い昨今、 それほど問題にならないのでは?と思います。 むしろアライメントすることでメモリアクセスの冗長さを減らせて帯域を節約する効果も あって一石二鳥ではと思うんです。
708 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 11:59:03.43 ] gccだと-mpreferred-stack-boundary=4がデフォルトだから既に16バイトアライメントだよ 構造体の詰め物は互換性もあるし難しいじゃないか
709 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 12:28:16.46 ] >>707 互換性
710 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2011/12/23(金) 12:41:37.02 ] >>707 なにそれ君いまだにPS3向けのゲームとか組まされてるわけ?
711 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 15:57:24.48 ] >>708 glibcのmallocは8バイトだよ >>710 SandyBridgeでもアラインメント取れてないと遅いでしょ
712 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 16:00:51.42 ] 構造体はCの規格だと順番を入れ替えられないだけで詰め物はし放題だよ 実際4バイトアラインメントのアーキテクチャだとコンパイラが詰め物するでしょ
713 名前:デフォルトの名無しさん mailto:sage [2011/12/23(金) 17:19:17.60 ] >>711 Nehalem以降はmovups/dquもペナルティ無く使えるでしょ まあ結局Core2以前も考慮するとコード振り分けるから労力は変わらないのだけれど...
714 名前:707 mailto:sage [2011/12/24(土) 02:12:26.59 ] すみません、自分は日曜プログラマレベルで、対象CPUはx86、環境はVC++です。 VC++の設定を見ていると、「構造体メンバのアライメント」というのがあって16バイトアライメントを選べるようになってました。 同様に通常の変数もアライメントできる設定があるかと思い探しましたが見付かりませんでした。
715 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 02:18:17.32 ] どう考えてもこれ以上削れないってくらいの手書きインラインアセンブリコードに対し、 C記述版をVC++のReleaseモード(最適化O2)でコンパイルしたもののほうが1.3倍速かったです。 生成されたアセンブリを覗いてみたところ、 変数 * 3 というコードを lea edx, DWORD PTR [eax+eax*2] としていてびっくりしました。 これって、アドレス演算を行うローダ(専用の演算器?)を使うことで、 通常のALUと並行して演算(スーパースカラって言うんでしたっけ?)し 高速化しているということなんでしょうか??
716 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 02:34:46.72 ] >>714 __declspec(align(N))
717 名前:707 mailto:sage [2011/12/24(土) 02:44:05.59 ] >>716 ありがとうございます。 それは知っているんですが、>>707 でも書いた通り、 そういった記述をわざわざせずとも、自動で全てアライメントしてくれるような設定があればイイのでは? と思った次第です。 しかし、上でも仰られたように、互換性の問題があったりで難しいのでしょうね・・・ でも二重インクルード防止の「#pragma once」のように、互換性を考慮しない機能があったりするくらいですから、 自分のようにずっとVC++しか使わない人間を対象にそういうオプションが用意されていても良いのではと思いました。
718 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 04:29:51.40 ] >>715 ついでにPartial flags stallも回避できるな
719 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 05:22:08.10 ] >>717 キャッシュミスが多くなったら速くならない。 だから隙間が多すぎるのは良くない。 まだそのレベルの高速化を気にするレベルじゃないと思うが、気になるなら続きは↓とかで。 toro.2ch.net/test/read.cgi/tech/1322981274/ toro.2ch.net/test/read.cgi/tech/1302223650/
720 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 06:03:11.07 ] >>713 Nehalemでmovupsのペナルティがなくなったのはアラインメントされている場合だけ Penrynまではアラインメントされていてもmovapsより遅かった ハードウェアがどういう実装になっているのかをよく考えてほしい さらにSandyBridgeでは非アラインメントの場合 2つあるロードユニットが生かされないばかりか追加ペナルティもあるので アラインメントされている場合に比べて非常に性能が落ちる movaps/movups使い分けの時代はSegmentation Faultの温床だったので movupsオンリーで書ける様になったのはうれしいよね Haswellで32Byteアラインメントが出てくるかと思うと頭が痛いが
721 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 06:12:36.27 ] >>716 VCの__declspec(align(N)) や gccの__attribute__ ((aligned(N)))は スタックやベースポインタからのアラインメントは保証されるけど ヒープにどう確保されるかは保証されないよ ランタイムライブラリ依存だからね そのクラスのnew/deleteをオーバーロードするか 大雑把にグローバルでオーバーロードするかしないといけないが どちらも一長一短ある
722 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 08:19:59.68 ] >>717 ぶっちゃけそれやってどれだけの速度向上が望めるの?って事情もあるんじゃねーの たかだか1MBに満たないスタックに、って 最近のCPUだとunalignedアクセスの性能低下も軽減されて古いCPUだと速くなるって言われてもなーって気持ちもあると思う
723 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 08:56:50.23 ] SandyBridgeもL1D$は1R1Wの4bank構成で うまくバンクコンフリクトを避けないとペナルティがある 非アラインメントのmovupsは2bank使ってしまう
724 名前:デフォルトの名無しさん mailto:sage [2011/12/24(土) 12:22:00.92 ] >>715 手書きでもふつーにやる。
725 名前:715 mailto:sage [2011/12/24(土) 14:11:41.35 ] >>718 おお、そんなメリットもあるんですね。 >>724 覚えておきます。 コンパイラの吐くアセンブラを見てると眩暈がしてきましたw SIMDやプリフェッチ等のキャッシュ制御を除けば、 手書きでアセンブリ書くのはほとんど効果的でないような気がしてきました。
726 名前:707 mailto:sage [2011/12/24(土) 14:22:27.84 ] >>719 なるほど、バイト境界を跨いでしまってアクセス回数が増えることより、 キャッシュに収まって低レイテンシでアクセスし易くなる効能のほうが上回るわけですね。 いかに外部DRAMへのアクセスがコストになっているかがよく分かります。 あと、スレ誘導もありがとうございます。 >>722 >最近のCPUだとunalignedアクセスの性能低下も軽減されて そうだったんですか。 それだったらmovupsでいいですね。 でも>>723 さんの御意見も気になりますので、パフォーマンス測りながら追い込むようにします。
727 名前:デフォルトの名無しさん mailto:sage [2012/01/10(火) 00:59:58.08 ] >>720 64ビットだと、当初MMXが使えなくなるとか言ってた関係で VCはMMX用の組み込み関数が使えないんだよなぁ でもって、MMXをSSE2に書き換えるとメモリオペランドのアライメントで 例外が発生したりするんだorz
728 名前:デフォルトの名無しさん mailto:sage [2012/01/10(火) 01:14:13.06 ] もう最初から全部、256バイト境界にアライメント揃えて実行ファイル作っとくかw
729 名前:デフォルトの名無しさん mailto:sage [2012/01/10(火) 04:02:17.42 ] グローバルのnewをオーバーロードしたくなる時は結構ある
730 名前:デフォルトの名無しさん mailto:sage [2012/01/10(火) 09:57:01.67 ] 32bitOSを切り捨てたい時もよくある
731 名前:デフォルトの名無しさん mailto:sage [2012/01/10(火) 11:53:57.38 ] intやポインタが64bitになっても面倒なだけだと思うけどな。
732 名前:デフォルトの名無しさん mailto:sage [2012/01/10(火) 13:24:45.38 ] お前がそう思うんならそうなんだろう お前ん中ではな
733 名前:デフォルトの名無しさん mailto:sage [2012/03/09(金) 12:53:25.77 ] unsigned int maxpos(unsigned int src[256]) { unsigned int i, m = 0; for(i = 1; i < 256; i++)if(src[m] < src[i])m = i; return m; } これをSSEで高速化する方法があれば教えて下さい
734 名前:デフォルトの名無しさん mailto:sage [2012/03/09(金) 14:27:48.76 ] byte配列ならPHMINPOSUWで速くなりそうだけど、そのコード基本的に検索だからSSE向きではないな。 最大値求めて、それを検索して位置返すのもありだが、速いかは知らんw
735 名前:デフォルトの名無しさん mailto:sage [2012/03/09(金) 15:23:00.43 ] >>733 src[m]をキャッシュした方が速いと思う。 要は、SSE以前にやることやってからにしろ。
736 名前:デフォルトの名無しさん mailto:sage [2012/03/09(金) 17:16:54.28 ] unsigned intだとSSE4使わないと素で書いたより無駄に遅くなりそうなのしか思いつかないわ
737 名前:デフォルトの名無しさん mailto:sage [2012/03/10(土) 14:03:19.33 ] SSE使った方が遅くなるのは何故でつか?
738 名前:デフォルトの名無しさん mailto:sage [2012/03/10(土) 18:35:29.43 ] 日本の道路をF1で走るようなもんだから
739 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 00:41:15.69 ] >>733 画像とか統計の基礎だな。疑似コードで書くとこんな感じだ maxpos(src[256]) { pos = {0, 1, 2, 3}; for(i=0; i<256; i+=4) { s=load(&src[i]); isGT=maxVal<s; maxVal=isGT&s | ~isGT&maxVal; maxPos=isGT&pos | ~isGT&maxPos; pos += 4; } return max_position( maxVal[0], maxPos[0], maxVal[1], maxPos[1], maxVal[2], maxPos[2], maxVal[3], maxPos[3]); }
740 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 01:57:27.28 ] >>739 SSEは max命令とunpackv命令があるんだから 32bitのsrc[i]値を64bitのsrc[i]<<32|iに変換して最大値を求める方が速いと思うね
741 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 02:00:06.23 ] 740だが、64bitのmax命令はないんだな それなら >>739 でいいと思う スカラーコードより速く すまん
742 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 12:51:45.57 ] >>739 signedの比較命令しかないからダメじゃない?
743 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 13:56:40.66 ] なんか知らんが、そんなのずらせば済む事だろ
744 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 15:40:49.12 ] 実際にコード書いて検証しようぜ。 思い込みと現実は違う場合が結構あるしさ。
745 名前:デフォルトの名無しさん mailto:sage [2012/03/11(日) 16:00:05.53 ] 古いPCを使ってるからかもしれんが>>733 を単純にアンロールしたのが一番速いな
746 名前:デフォルトの名無しさん mailto:sage [2012/03/12(月) 00:37:37.24 ] スレチではあるが、このコードを高速化する必要がある処理を見てみたい気がした。
747 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2012/03/12(月) 04:51:41.06 ] >>742 0x80000000を引く。 足してもいいしXORでもいいけど。
748 名前:デフォルトの名無しさん mailto:sage [2012/03/12(月) 19:18:44.41 ] 横レスだけどおおすげー。 そもそも正数同士・負数同士の比較ならsigned/unsignedで結果は変わらないので 問題は正数と負数の比較のみ。確かに最上位ビットを反転させれば万事うまくいくね。
749 名前:デフォルトの名無しさん mailto:sage [2012/03/12(月) 19:42:04.67 ] 256個ぐらいだと、最大値を見つけて最大値と同じのを見つけるのだと遅いかな
750 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 01:41:38.96 ] signedとunsignedの変換が凄いのか。 この手のテクをまとめようと思ったらどのレベルから書いていいか分からんな。
751 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 02:23:57.53 ] >>749 俺は実装が面倒という理由でそれでやってるが やっぱメモリに2週アクセスするから遅いのでは
752 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 05:34:13.85 ] pxor x2+pcmpgtbとpblendvbで最大値のインデックスを保存 インデックスは0x01010101でインクリメント 最後に4要素を比較 4要素あたり2サイクルで回るかな signed intだったら4要素あたり1サイクルでいけるかも? unsignedでもcharだとpsub+pblendvbだけでいけるからよいんだけどね
753 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 05:36:23.52 ] あpmaxudも必要か
754 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 05:54:27.09 ] __m128i pos = _mm_setzero_si128(); __m128i maxpos = _mm_setzero_si128(); __m128i maxsrc = _mm_set1_epi32( 0x80000000 ); #pragma unroll(16) for( int i = 0; i < 256; i+=4 ) { __m128i signed_src = _mm_xor_si128( _mm_load_si128( (__m128i*)&src[i] ), _mm_set1_epi32( 0x80000000 ) ); __m128i mask = _mm_cmpgt_epi32( signed_src, maxsrc ); maxpos = _mm_blendv_epi8( pos, maxpos, mask ); maxsrc = _mm_max_epi32( maxsrc, signed_src ); pos = _mm_add_epi32( pos, _mm_set1_epi32( 1 ) ); } 以下略 実行してないのであってるかは知らん
755 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2012/03/13(火) 12:31:21.86 ] > unsignedでもcharだとpsub+pblendvbだけでいけるからよいんだけどね その程度ならpacked floatにキャストしてblendvpsでもいけるけどね。 SSE4対応以前ならpmovmskb/maskmovps+test+jccでもいけるか (メディアンならともかく最大値ならcmovより分岐のほうが速いはず)
756 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 14:51:08.23 ] >>755 pbledvbは7+8nのビットしか読まないから符号の判定に使える SSE4以前だとmaskにはand+andnot+orが使われてたね
757 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 14:52:50.84 ] Penrynだと下手にblendv系使うより速い場合もあったりした
758 名前:デフォルトの名無しさん mailto:sage [2012/03/13(火) 14:59:06.94 ] ごめんblendvpsも31+32nビットしか読まないのか 勘違いしてた 符号の判定に使えるね
759 名前:デフォルトの名無しさん mailto:sage [2012/03/14(水) 00:38:46.60 ] >>757 PenrynのPBLENDVBはレイテンシ1、スループット2で SandyBridgeはレイテンシ2、スループット1か… Penrynはデコーダの制限もありそうだし、and,orでもよさげ
760 名前:デフォルトの名無しさん mailto:sage [2012/03/14(水) 00:46:50.97 ] HTが使えるCPUならblendの方がいいかな?
761 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2012/03/14(水) 07:19:23.65 ] どうせ全検索するんだしレイテンシの長さ分だけインタリーブすればレイテンシ隠せるよ
762 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2012/03/14(水) 07:23:58.09 ] packed unsigned intの比較(マスク生成)だけど、両項のMSBを反転してからpcmpgtdするより psubd + psradのほうが速いかもしれない
763 名前:デフォルトの名無しさん mailto:sage [2012/03/14(水) 09:52:48.15 ] >>762 どっちも2命令でXORの方が実行出来るポートが多い分有利そうに感じるのだが
764 名前:デフォルトの名無しさん mailto:sage [2012/03/14(水) 20:30:50.02 ] pdangod
765 名前:デフォルトの名無しさん mailto:sage [2012/03/19(月) 10:59:48.11 ] >>762 blendvps用のマスクならpsubdだけでよい blendvpsは最上位ビットしか見ないから
766 名前:デフォルトの名無しさん mailto:sage [2012/03/21(水) 21:47:07.57 ] 話の流れぶった切るけど団子さんよ〜。 今の静的分岐予想って相変わらずifよりelseの方が速いの? ifとelseのどっちが速いか正確に計測するにはどういうコード書いたらいい?
767 名前:デフォルトの名無しさん mailto:sage [2012/03/21(水) 23:28:17.45 ] あれ?ifの方が速いんじゃなかったか
768 名前:デフォルトの名無しさん mailto:sage [2012/03/21(水) 23:53:20.16 ] P6,P2,P3,P4はforwardならnot taken backwardならtakenがデフォルト P4はPrefixでヒントを出せる PM,Core2はランダム ソースはAgner
769 名前:,, ・´ ∀ `・ ,,)っ-○○○ mailto:sage [2012/03/24(土) 09:22:43.82 ] 分岐予測履歴がない場合あるいは予測そのものがない場合、条件付ジャンプは前のアドレスに 飛ぶ場合(多くの場合ループ)は原則ジャンプ、後ろならスルーが多くのCPUの実装ですね。 大体のコンパイラってforやwhile文はこんな感じに展開するでしょ? if (cond) { do { ... } while(cond); }
770 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 12:02:04.61 ] >>769 モダンなCPUはどんな感じなのよ >>768 はランダムだと言ってるけど モダンなCPUでも静的予想だとifはスキップ whileはループ確定なのけ。
771 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 19:11:37.76 ] 分岐履歴が無いのに前方への分岐を予測したら命令フェッチをやり直さなきゃならないじゃん 後方への分岐はループの場合が多いから特別扱いなんだと思う
772 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:14:58.62 ] 大概returnやthrowが行われるからifをすっ飛ばせば速いのは解る。 反復も反復する事を優先した方が早いのは解る。 それはいいとして、今のCPUは分岐ヒントとか投棄とかあって単純じゃないんだろ。 そこを知りたいんだがね。
773 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:25:07.37 ] >今のCPUは分岐ヒントとか投棄とかあって単純じゃないんだろ。 パイプラインが深いだけでしょ
774 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:33:04.06 ] わかってないのにわかったつもりになって 単語だけ並べているように見えるのは何故だろう
775 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:41:50.71 ] そう思ってくれるのはいいが、間違いの指摘と 実際はどうなってんのか答えてくれ 批難だけの回答はいらん。
776 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:45:20.20 ] >>773 分岐予測の話はパイプラインが深いこと前提で話してるわけで・・・
777 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:50:30.10 ] >分岐予測 って、言い方してるだけなの?
778 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:52:06.49 ] >>775 じゃあ間違いを指摘してやる。 >大概returnやthrowが行われるからifをすっ飛ばせば速いのは解る。 前方への条件分岐は、「分岐しない」と予測される、とオマエ以外の全員が言っている。 if (xx) return で「すっ飛ばせば云々」なんて、理解していないまま「わかったつもりになってるだけ」の証拠。 >今のCPUは分岐ヒントとか投棄とかあって単純じゃないんだろ ヒントはともかく、投機はまさに「投機実行するために分岐予測をする」のであって 「投機実行もあるから分岐予測が複雑になる」はナンセンス。 もちろん、エンプラ系/VLIW系では「分岐の有無の両経路を実行する」なんてのもある(らしい)が 一般的とは言いがたい。
779 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 21:59:34.64 ] >>778 内容じゃなく国語的に誤解されてるな。 >前方への条件分岐は、「分岐しない」と予測される、とオマエ以外の全員が言っている。 if( xx ) throw xxx; throwなんて実行するケース殆ど無いんだから基本if実行しないってのは同じ話。 矛盾してないでしょ。 まず分岐予測が複雑になってるって話はしてないよ。 投棄実行については投棄実行を考慮した上での静的予測方法があるでしょという話。
780 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:04:11.41 ] >>779 だから、ifの内部は「実行すると予測される」んだよ、バーカ
781 名前:779 mailto:sage [2012/03/24(土) 22:05:34.03 ] 尤も、わかりやすいからifと書いたけど、 実際はコンパイラの最適化でif、elseは反転するから この言い方は正しくは無いんだけどね。
782 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:06:41.25 ] せめて正しい漢字使えよ。 一度なら単なる変換ミスとして納得できるけど 繰り返しているってことは、別の意味に捉えているとしか思えない。
783 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:09:32.88 ] >>780 お前バカだろ みんなアセンブリ前提で言ってんだよ jze label if() { ・・・処理・・・ labeli }
784 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:12:25.76 ] >>782 ん?尤も(もっとも)か?無い(ない)か?
785 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:20:41.79 ] >投棄実行については投棄実行を考慮した上での静的予測方法があるでしょ 理解できる日本語で書いてくれないかな。 分岐予測というのは、投機的に(結果が判明する前に)実行する前提でのもの。 (無条件分岐や間接分岐もあるから、厳密には正しくないけど) 予想して(投機的に)実行するのでなければ、単に結果が判明するのを待って、それから実行すればよい。 つまり、投機実行するからこそ分岐予測が必要なのであって 投機実行しないのであれば、分岐予測など元々必要ない。 そのことをちゃんと理解していれば、 そもそも「投機実行を考慮しない分岐予測」などというものが存在しないから >投棄実行を考慮した上での静的予測方法 が意味不明に感じられる気がするんだけどな。 >>783 わかってるよ、そんなこと。
786 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:22:22.48 ] >>780 ifが優先されると思ってたのは>>767 とお前ぐらいだよ
787 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:25:19.71 ] >>785 ごめん。もう今更何を言おうが>>780 のせいで信用ないわ。
788 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:34:54.17 ] >>782 ああ投棄実行ね投機の変換ミスそのままにしてたわ
789 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:48:33.55 ] 投棄実行については誤解してたわ。 2つの分岐を両方実行して実際実行対象にならなかった方の結果を破棄するものだと思ってた。
790 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 22:50:38.87 ] >大概returnやthrowが行われるからifをすっ飛ばせば速いのは解る。 もう一度考えてみたけど、この意味が全然理解できない。 ぱっと見で、何故returnや、 増してthrowなどという言葉が出るのかわからない。 (GP等の例外の割り込みは全く別物) で、次の行の >反復も反復する事を優先した方が早いのは解る。 の意味は、「ループであることが推測されるから分岐すると予測する」だよね? それとの対比で、「ifをすっとばせば」の意味が 「ifの内部は実行されないと予測」と捉えれば 「ifの部分での分岐は分岐すると予測すれば if内部のreturnやthrowに制御が来ないので別の分岐の予測を避けられる」という意味にもとれる。 それならようやく意味が通じるような気がして、そういう意味だと思ったんだけど。 つまり、ifの部分(=前方分岐)を「分岐すると予測する」という意味だと。 でもそれ(前方へ分岐すると予測する)は実際のプロセッサの動作とは違うわけで ならばどういう意味なのか、さっぱりわからなくなってしまってね。
791 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 23:00:43.72 ] お前の文章が解らんわ
792 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 23:01:04.27 ] 一応。 突然returnやthrowが出てくるのが if (xx) return という意味じゃないか、というのも勝手に俺が頭の中で補って想像しただけで 実際には何の説明も無く(ifの多くがreturnやthrowというのにも同意しにくい) 唐突な「rerturnやthrow」「すっ飛ばす」を必死に理解しようとしたのがそもそもの間違いかもね。
793 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 23:01:51.31 ] 実行時の分岐予測って、どうなってるのかわかってないと、グダグダでしょ
794 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 23:04:39.09 ] >>792 >>779 で補足だしてるだろしつこいわ
795 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 23:09:08.21 ] >>790 >>792 ウゼェ・・・
796 名前:デフォルトの名無しさん mailto:sage [2012/03/24(土) 23:18:44.32 ] >>794 >>779 だとしたら>>780 だっての。 「前方分岐は分岐しないと予測する」のが実際のプロセッサの動作なわけ。 >>779 の「throwなんか滅多に起こらないから実行しない(と予測する)」は 「前方分岐は分岐すると予測する」ことになるから実際の動作と矛盾するという話。 それを、「わかってないくせに」の根拠の一つにしたんだよ。 実際、「分岐予測とはどういうものか」も「なぜ分岐予測するのか」もわかってなかったわけでしょ(>>789 )。
797 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 01:09:12.01 ] 口汚い言葉で罵倒せずには居られない奴って下品・・・。 団子さんの紳士っぷりを少しは見習ったらどうだろうか。
798 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 01:16:07.87 ] もう>>780 はいいから団子さんこないかな
799 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 01:24:14.75 ] え?指摘するの780だけ? 露骨だなぁ。恥ずかしくないんだろうか。
800 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 02:30:29.23 ] お前がなwww
801 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 03:19:03.04 ] 頭が悪すぎて理解できなかったらしいから仕方ないな。
802 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 14:12:21.08 ] >>796 投機実行という言葉については誤解してました。 分岐予測については、分岐ミスでパイプラインに読み込んだ 命令を破棄が発生するという認識です。 そもそも、それが気にならないのであれば分岐予測なんて 気にする必要はないでしょう。
803 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 16:12:43.94 ] インテルの最適化マニュアルだと >インテルPentiumM プロセッサー、インテルCoreSolo プロセッサー、インテルCoreDuoプロセッサーは、 >ジャンプの向きに従った条件分岐を静的には予測しない。これらのプロセッサーでは、 >すべての条件分岐は、最初に発生したときでも動的に予測される。 と書いてあって、wikiで調べたらPenM以降は広域分岐予測を取り入れた関係で静的予測はしなくなったみたいだね ttp://ja.wikipedia.org/wiki/%E5%88%86%E5%B2%90%E4%BA%88%E6%B8%AC >>768 の >PM,Core2はランダム というよりも、「前に実行した別の分岐命令の結果も影響する」とした方が適切みたいだ 分岐予測に関して ttp://news.mynavi.jp/column/architecture/index.html の第167回からの解説が参考になるよ
804 名前:デフォルトの名無しさん mailto:sage [2012/03/25(日) 18:09:59.76 ] >>803 なるほど。いい話が聞けました。ありがとうございます。
805 名前:デフォルトの名無しさん mailto:sage [2012/03/28(水) 00:06:00.96 ] >>803 >と書いてあって、wikiで調べたらPenM以降は広域分岐予測を取り入れた関係で静的予測はしなくなったみたいだね P4もグローバル履歴を使ってるよ おそらくだけど ある分岐命令を最初に実行したかどうかはBTBのエントリが割り当てられているかで判断するので 分岐先予測より分岐予測のヒット率が十分に高い場合は動的分岐予測が当たる条件で誤って静的予測してしまう確率が高いから 1回目の分岐予測を諦めても静的予測をやめたほうがヒット率が上がるということなんじゃないだろうか
806 名前:デフォルトの名無しさん mailto:sage [2012/03/28(水) 00:11:22.64 ] ×分岐先予測より分岐予測のヒット率が十分に高い場合 ○BTBのヒット率より分岐予測のヒット率が高い場合