- C++相談室 part129 [無断転載禁止]©2ch.net
719 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 01:10:03.99 ID:7dJ8iAqk0 - >>714
日本語で説明しようとしたのはまずかった。C++で書き直すと、以下。 ただしC++文法には詳しくないので、syntaxErrorは適宜脳内修正よろしく。 意味だけ見てくれ。ソースは>>666から流用してる。 class F { int _a; public: F(const int a):_a(a) {} int calc(const int x, const int k) const { return k * x + _a; } }; int main() { const F f0(1); const F f1(2); std::function<...> f0_1 = std::bind(&F::calc, f0, _1, 3); // 第2引数を固定 std::function<...> f0_2 = std::bind(&F::calc, f1, 4, _1); // 第1引数を固定 std::function<...> f1_1 = std::bind(&F::calc, f1, _1, 5); // 第2引数を固定 call_sub(F0, f0_1, f0_2); call_sub(F1, f1_1, f0_1); } void call_sub(F& f, std::function<...> f1, std::function<...> f2){ f->func(1,2); // (L) 直接呼び出し f1(3); // (M) コンパイラによっては直接呼び出し、一般的には2段階呼び出し f2(4); // (N) 2段階呼び出し }
|
- C++相談室 part129 [無断転載禁止]©2ch.net
720 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 01:10:33.50 ID:7dJ8iAqk0 - (L)は必ず直接呼び出しになる。アセンブラなら push, push, push, call
(M)はソース上を精査すれば同型呼び出ししかないことが分かるので 技術的には(L)と同程度まで最適化可能ではあるが、一般的には無理だと思う。 2段階呼び出し時は push, push, push, call, mov, jmp (N)は異なる形の呼び出しが与えられているので、 技術的に(L)と同じコスト(アセンブラ4命令程度)の直接呼び出しは不可能。 無理にcmpとかやるより普通に2段階呼び出しの方がいいと思う。 だからVCもそうしていると見ている。 それで、std::bindについては詳しい奴が以下見れば実装の見当が付くはずだが、 俺はC++用語に詳しくないので分からん。 http://ja.cppreference.com/w/cpp/utility/functional/bind 俺が実装するなら、ありがちなのをテンプレートで実装するが、 その場合はポインタサイズはsizeof(int)*2になる。 (メンバ関数へのポインタと、bind済みの値をstructで持つ=内部的な匿名クラスにするから) VCがなぜ4に出来るのかは分からん、というか計測間違いじゃないかと思う。 (バインド済みの値の分を入れてない)
|
- C++相談室 part129 [無断転載禁止]©2ch.net
721 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 01:11:05.58 ID:7dJ8iAqk0 - 俺が言いたかったのは、上記(L),(M),(N)ということ。
君の主張は、すべてbind時で確定して、上記(M)(N)も(L)と同じコストで呼べるという主張だと読める。 それは無理。 確かにアドレス自体は確定はするが、 引数をbindしているのだからスタックをその形にしてやらないといけない。 上記(N)の呼び出し、アセンブラ4命令ではどうやっても書けないだろ。 cmp使って書くくらいなら2段階呼び出しで6命令(2分岐)の方がマシ。 cmp使って無理に直接呼びにしたら、 そこに来る可能性のある呼び出し型を全部そこに書かなければならなくなる。 そしてx86はプレディケートを持ってないので、cmpで分岐しないといけない。(ARMは持っている) それって完全に本末転倒だろ。 それで、std::bindと関数オブジェクトをどう組み合わせたら、 (L),(M),(N)について上記通りアセンブラ4命令程度で呼び出せるのかよろしく。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
722 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 01:17:42.85 ID:7dJ8iAqk0 - すまん、重要なところを間違えていたので訂正。
>>719 > std::function<...> f0_1 = std::bind(&F::calc, f0, _1, 3); // 第2引数を固定 > std::function<...> f0_2 = std::bind(&F::calc, f1, 4, _1); // 第1引数を固定 > std::function<...> f1_1 = std::bind(&F::calc, f1, _1, 5); // 第2引数を固定 の2つ目、インスタンスは f0 ね。 std::function<...> f0_2 = std::bind(&F::calc, f0, 4, _1); // 第1引数を固定, f0 ただ結局、変な引っかけみたいになっているだけで意味は同じかな? まあ訂正しとくけど。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
725 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 01:44:23.31 ID:7dJ8iAqk0 - bindの説明読む限り、やっぱこれかなり汎用に作っていて遅いと思うぞ。
ただ一般的にはそこが見えるほど使うことはないと思うが。 アセンブラ的には、 f->func(): 4命令程度(分岐1回) std::bind経由での呼び出し: 10-50命令程度か?(分岐2回以上) 内部オブジェクト作成方式: 6命令程度(分岐2回) じゃないかな。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
728 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 02:14:29.60 ID:7dJ8iAqk0 - 誰かと思えば>>678か。
じゃあついでに言っておくと、以下だね。 callbacks.push_back([&]{a.func();}); // (O) クロージャ作成が無駄 callbacks.push_back(a->func)); // (P) この書き方は現在出来ないが、こっちの方がいい JavaScriptでは(O)は無駄だとされる。クロージャが余分にメモリを食うから。 クロージャ無しで書けるならその方がいい。コード領域も無駄だし。 これはC++も同じだし、C++の方が厳しいはずだが。 とはいえstd::bindの仕様が重すぎるのでこちらもイマイチだ。 C++ならどっちが重いかは微妙だね。 なおJavaScriptのbindは「前から順に固定される」だけの仕様なので、 固定した引数分のデータ領域しか各bindインスタンスには必要ない。 というかC++のbindが妙にリッチな仕様なのが謎だが。C++っぽくない。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
730 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 02:19:33.17 ID:7dJ8iAqk0 - >>727
俺には計算済みとしか見えないが、何か操作必要? -O3外せとかか? > fn(int): > lea eax, [rdi+rdi] > ret > bind(): > mov eax, 4 > ret > direct(): > mov eax, 4 > ret
|
- C++相談室 part129 [無断転載禁止]©2ch.net
731 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 02:28:44.92 ID:7dJ8iAqk0 - >>727
というかな、根本的に間違ってると思うぞ。 一般的に関数ポインタを使う場合、それは外部から与えられるんだよ。 自分の内部で作って使うって事はない。 (ここでは単純なソースで議論するからそうなるだけで、一般的にはそうではない。 つか、それじゃ関数ポインタの意味がないし) 典型的にはcallbackだよ。外部から与えないと全く意味無いだろ。 で、その場合は当然この手の事前計算とかでの最適化は出来ないんだよ。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
733 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 02:51:38.54 ID:7dJ8iAqk0 - >>732
あーお前がアホだということはよく分かったわ。 まともに議論する気なら、お互いが見ているものが同一であることを確認しないといけない。 >>727を見たらアホかと思うのは道理だろ。 そして>>732もアホかと思うよ。関数内部で何を作ってもそれは最適化されるよ。 それは一時変数を除去すればいいだけなのだから。 そうではなくて、例えば以下にしないといけない。 int main(){ auto f = std::bind(&something::func,ptr); bind(f); } int bind(std::function<...> f){ return f(); // bind済みのfを呼ぶコストをここで計測する } 再度言うが、関数ポインタは外部から与えないと意味無いんだよ。 てかお前ら実は全く関数ポインタ使ってないだろ。 だから使い方自体を知らないんだよ。 これで、bind/関数オブジェクト/クロージャで呼び出しコストを比べてみなよ。 当たり前だが上記だとbind呼んで終わりになるけど、 もちろんその先のターゲット(something::func)が呼ばれるまで見るんだよ。 つまりstd::bind内部のコードもね。それが呼び出しコストなんだから。 それで、あらかじめ計算済みとかいうアホなオチはマジで止めてくれ。 一般的にポインタを与えた場合、それが無理なことくらい分かるだろ。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
740 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 08:46:03.88 ID:7dJ8iAqk0 - 訂正ね。
>>733 × std::bind内部のコードもね。 ○ std::functionのoperater()の内部のコードもね。 処理系の実行コードも、という意味で書いたが、 std::bindはstd::functionを返すので今回についてはoperator()のコードになるのだよな? まあ間違っていたら適宜訂正してくれ。分かる範囲だと思う。 >>734 > std::bindに余分なコストなんてない。std::functionの方にある。 これが上記について言っているのならその通り。 ただそれは揚げ足取りだと分かるだろ。それがやりたかったのならどうぞ、おめでとう。 それが分からないのなら君にはこの話は無理だね。 というわけでいずれにしてもこの話は終わりでいい。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
741 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 08:47:04.27 ID:7dJ8iAqk0 - 一応中身は確認した。
>>734については呼び出し側は全部同じコードが出るという事ね。ここまではいい。 問題はその先だ。それは君も分かってるんだろ?だったら何故それをしない? >>735 > なぜか一時変数だから最適化できると信じてるみたいだけど、そう思うなら これについては相変わらずそちらが間違いだね。 事前計算済みのコードをドヤ顔で出してきたのはそちらだろ。 それはコンパイル時に「事前計算が出来る」ということがコンパイラにも分かったから。 とはいえ、君みたいな奴も沢山いるのも事実だよ。 > と書いてみればいい。 書いてみたらstd::functionのコードが出た。 中身は今の俺にぱっと分かるようなものではないね。 が、まあ、100命令弱増えているからその分遅いと言える。 だから結論は、直接=ファンクタ<<<<bindになるのかな? ファンクタは正直ピンとこない。 あとは>>729で無駄がないとドヤ顔のラムダがどこまで速いかだね。 まあ頑張って。 いずれにしても、この話は終わりでいい。 俺が確認したい範囲はもう分かった。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
745 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 09:17:50.63 ID:7dJ8iAqk0 - >>743
だから俺はそこら辺までは詳しくないのさ。 繰り返しているとおり、std::bindの説明を読んでも全てを今すぐ理解する知識はないんだよ。 俺はC+JavaScriptだからね。C++はbetterCとして使ってきてるが、 VC++(=CLI)だからSTLを使ったことはなく、.NETだし。 C++特有のテンプレートとか型消去については 「動的型なら何も考える必要ないのに」で飛ばしてきたし、 RAII+スマポについても「そこまで言うならGC言語使えよ」としか思ってない。 だから今、型消去とかも読んでるけど、これを理解するには時間がかかるし、 上記の通りSTLについてもほぼ知らないから、これまたインストールに時間がかかるんだよ。 > std::bindの返りの型はstd::functionでは”ない” まあそれはさておき、std::bindは何が返るんだこれは? 型消去で無理矢理格納出来るだけなのか? ただ、実行時はstd::functionのoperator()が呼ばれると思うが、これも違うのか?
|
- C++相談室 part129 [無断転載禁止]©2ch.net
747 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 09:41:13.37 ID:7dJ8iAqk0 - >>744
あー何となく分かってきた。以下ページ、 http://ja.cppreference.com/w/cpp/utility/functional/bind の「メンバ関数 operator()」ってとこが呼ばれるのか。つか英語版見るべきだったかも。 じゃあ当初の「std::bind内部のコードもね」でも十分通じる気もするが、まあこれはいい。 いずれにしても俺はこの通り、cppreferenceの見方もよく分かってない。 だから君らに追いつくのは時間がかかる。 > コストは 直接=ラムダ=bind <<< std::function ラムダは最高に最適化された状態でbindと同じ。 bindもoperator()の内部でプレースホルダ引数の配置をするのなら、 同様に最高に最適化された状態で直接と同じ。 結果、直接=<bind=<ラムダ じゃないか? まあこれについてはそちらの方が詳しいだろうし、こちらも引き続き詳細確認中だが。
|
- C++相談室 part129 [無断転載禁止]©2ch.net
748 :デフォルトの名無しさん (ワッチョイ f35b-tpgq)[sage]:2017/03/19(日) 09:49:17.20 ID:7dJ8iAqk0 - ちなみに誰かと確認してみたが、
> std::bindを見てECMAScriptのbindingと同じものだと勘違いしていただけか。(>>688) なるほど、その通りだ。俺が思っていたbindとはだいぶ違う。
|