[過去ログ] Go language part 5 (1002レス)
上下前次1-新
抽出解除 必死チェッカー(本家) (べ) 自ID レス栞 あぼーん
このスレッドは過去ログ倉庫に格納されています。
次スレ検索 歴削→次スレ 栞削→次スレ 過去ログメニュー
リロード規制です。10分ほどで解除するので、他のブラウザへ避難してください。
4(1): デフォルトの名無しさん [sage] 2022/02/27(日) 08:59:11.36 ID:PVy06kKY(1/8) AAS
>>992(前スレ)
読んだ。で、やっぱり奇妙なんだけど、多分オーバーヘッドはないと思うよ。
一般的にはガードページなんて必要なくて、コピーオンライトと同じで、
ページ境界を跨いだ場合はハードウェアで検出出来るから、まず普通はそれを使う。
この場合は自前でのチェックは必要ない(ソフトウェアには必要ない)ので、オーバーヘッドはない。
だからGoの当初の初期スタックサイズが4kだったのは非常に納得出来た。ここまではいい。
これを小さくするならハードウェアのサポート無しになるから当然自前でチェックするしかないが、この場合、
・2kも大きすぎ。自分でやるならRustのように64Bytesからとか、4kに拘らず凄く小さいスタックサイズから可能だし、普通はそうする。
・そもそも必要スタックサイズを予見出来ない。というか出来るならコンパイル時に確定的に割り当てれば済んでる。
であって、Rustの実装は非常に納得出来るのだけど、Goのは若干意味不明なんだよ。
(ただまあ何かしら理由はあると思うけど)
2kとかいう、4kに拘ったサイズになってるんだから、多分何かしらハードウェアのサポートを受けてて、
自前ではスタックサイズのチェックはしてないと思うよ。(つまりオーバーヘッドがない)
可能性があるのは、2kをはみ出る時には4k境界を跨ぐようにして(つまりまずは上側を割り当てる)
はみ出た時に2kずらしていくとかだけど。
ただこの方式の場合、初期アロケーションだけは4kでされてしまうので、957のベンチでは40MB越えないとおかしくて、矛盾してる。
だから正直よく分からないが、
多分オーバーヘッドのない方式で実装してて、だから2kとかいう中途半端な巨大サイズになってるのではないかと思う。
5: デフォルトの名無しさん [sage] 2022/02/27(日) 09:14:34.62 ID:PVy06kKY(2/8) AAS
>>998(前スレ)
分かりやすい説明ありがとう。発想は面白いね。
確かにその方式だと死蔵メモリは最小限に留められ、Goの問題は解決されてるはずだね。
>>986(前スレ)
998の通りだと、外部からのプリエンプションではなく、
コルーチンが処理を(自発的に)返してくるから、その単位での切換だろう。
まあこれでも現実的には問題ない気はする。
8: デフォルトの名無しさん [sage] 2022/02/27(日) 14:25:03.95 ID:PVy06kKY(3/8) AAS
>>992(前スレ)
訂正。他も色々漁って、やっぱりオーバーヘッドはあると思えてきた。
ただし多分0〜4cycle/関数呼び出し程度だね。
主に読んだのは以下とか。
https://github.com/tinygo-org/tinygo/issues/2000
https://dave.cheney.net/2014/06/07/five-things-that-make-go-fast
https://dave.cheney.net/2013/06/02/why-is-a-goroutines-stack-infinite
・当初は8kだったが、もっとでかくしろという要求が多かった
・1kに出来てる実装もあるが、こける事もあるから2kがデフォになってる
・同じ図が説明に使われてて、どうやら最初からこの方針らしい
本人達が「チェックしてる」って言うんだからやっぱりそうなんだろう。
一番ありそうな実装は、呼ばれた関数側で最初にチェックする方法で、
つまりローカル変数をスタックに確保するためにスタックポインタからサイズを引く時にチェックする。
これだと、 mov, (sub,) xor, and, jne の追加4命令で(subはチェック無しでも必要)
主にINTパイプで最悪で4cycle、大体の場合はこのあとのLSパイプ(スタック上へのレジスタ待避)に隠れて見えなくなるのではないかと。
だから関数呼び出し毎に0〜4cycのペナルティになる。
ただし、速くなる事はないし、隠れると言っても演算リソースは消費するので、
ハイパースレッディングの場合に相手側のCPUの速度が落ちるのは否めないが。
2k以下にもやれば出来るのだろうけど、やる気がないだけだね。
だから2kで問題なら、コンパイラをいじれば何とかなるのだろうけど、
そこまでやるくらいならRustの方が断然いいね。(後発だから当たり前だけど)
10(1): デフォルトの名無しさん [sage] 2022/02/27(日) 16:13:39.08 ID:PVy06kKY(4/8) AAS
>>9
> 高階関数らしい高階関数も無いし
いや高階関数は普通に出来る。
https://go-tour-jp.appspot.com/moretypes/25
標準やフレームワークにそれを活用する文化がない、という主張なら俺はその辺は知らん。
15(2): デフォルトの名無しさん [sage] 2022/02/27(日) 20:47:56.30 ID:PVy06kKY(5/8) AAS
>>11
> 結局のところ素直にforループを書くのがGoには適していて、
> 汎用の型に適用可能な高階関数を実装しようとするのはミスマッチ
これってforEachを実装しようとするのが間違いって事?
或いはmapに高階関数を食わせるのを嫌ってるのか?
いずれにしても、やりたきゃやれよ、だと思うが。
オレオレコーディングルール集だが、全般的に現状肯定的なので、
目指すコーディングではなく、今のGoコンパイラ/ランタイム向けのTips集になってる。
これがどれくらい賛同されるのか不明だけど、革新/改革派からは気に入らない内容だろうよ。
Python出身のようだから、この辺C系の「何でもやっていいけど、結果責任は取れ」文化とは根本的に違うのだろう。
一種類のコードになる事を良しとしてる。
> goroutiineのスケジューラはもちろん依頼処理コストが大きい場合をフォーカスしてチューニングされています。
> ひとつのgoroutineがCPUを占有しすぎないように分散してくれる仕組みがありますが、
> 小さすぎる処理コストをまとめるのは実装者の責任で行わなくてはなりません。
> 小さすぎる処理を頻繁に繰り返すだけであればシングルスレッドの方が速い結果が出るのは当たり前なのです。
これとか、1,000,000goroutine全否定だよね。俺は
Go信者:1,000,000goroutineでも軽いし速い←嘘つくな
GoUser:遅いけど1,000,000goroutineで書きたいんだよ←うむ、遅いと認めるならよろしい。しかし速くなる努力はしたまえ
> Goはデバッガビリティのために末尾再帰最適化をあえて実装していません。
いや怠慢でしかないだろ。再帰がデバッグしづらいとか聞いた事無いわ。
ちな、
> 自作ライブラリの利用方法をメソッドチェインで作ろうとする
考えた事無かったけど、try-catchのメリットってメソッドチェインできることか。
なるほどだからろくにメソッドチェイン出来ないPHPだと何ら意義を感じ取れなかったわけだ、納得。
17(1): デフォルトの名無しさん [sage] 2022/02/27(日) 21:41:54.35 ID:PVy06kKY(6/8) AAS
>>14
もしかして俺が前スレ962で単純に40MB足してたのが気になってたのなら申し訳ない。
あれは足しすぎだった。RSSの意味は以下。
https://stackoverflow.com/questions/7880784/what-is-rss-and-vsz-in-linux-memory-management
十分なメモリがある状況で普通に実行させた直後(スワップされてない状況)なら、RSSで問題ない。(はず)
以下は前スレ992内にある図だが
https://commons.wikimedia.org/wiki/File:Table_of_x86_Registers_svg.svg
これ全部を待避するのに1kB程度かかるらしく(真面目に数えれば正確な数値は出せるが、やる気無し)
この分をOS側が待避するので、単純に言えばスレッド数*1kB程OS側のメモリを食ってる。
これがgoroutineだと必要ない(Goランタイム管轄で待避で、RSSに計上されてる)ので文句付けられてる。
だから公平に見るなら、goroutineはRSSそのままで良く、OSのthreadを使うならスレッド数*1KB程度追加かと。
(40MBはスレッド数*4kBにしてるので、多すぎ。
アクセスのない、単なる待避領域なので、ページ単位である必要はない。)
そして関数呼び出しのオーバーヘッドについてはそこにモロに書いてあるな。(GitHub上ソースの17行目〜60行目)
0〜4Cycleのオーバーヘッドになる。
方式としては、スタックの底に96Bytes(=40+56)の領域があらかじめ確保してあって、
これらはメモリが足りない時に呼ばれるdeferproc()とmorestack()に必要なスタックサイズなのだが、
逆に言えば96Bytes以下のスタックしか使わない関数ならスタックポインタがそこを越えてなければ問題ないわけで、
以下チェックを通してる。(guardがスタック満タン-96Bytesのアドレスを示してる)
> CMPQ guard, SP
> JHI 3(PC)
> MOVQ m->morearg, $(argsize << 32)
> CALL morestack(SB)
まあスタック増加がなければINT/BR/NOP/NOPなので、オーバーヘッドは通常1か2Cycleじゃないかと思うけども。
18(1): デフォルトの名無しさん [sage] 2022/02/27(日) 22:00:44.12 ID:PVy06kKY(7/8) AAS
>>12
ちなみにコードが素晴らしくメンテされてれば、
_StackMin = 1024
にするだけで、スタックサイズが1kBになるような気もします。
20: デフォルトの名無しさん [sage] 2022/02/27(日) 23:03:35.02 ID:PVy06kKY(8/8) AAS
>>19
いやあれは「べからず集」なのだから否定だと思うぞ。
まあいいが。
> 普通はデバッグビルドだと末尾再起無効では?cppなんかでも。
そもそもデバッグビルドなら『全ての』最適化は無効だ。そしてその状態で全てのデバッグを行う。
それでリリースビルドでは一発で動く、というかデバッグしないのが基本だ。
(同じ出力を生成する事だけを確認する)
「デバッグビルドでは動くのですが、リリースビルドでは動きません。これってコンパイラのバグですよね?」は初心者あるあるだが、死ねでいい。
リリースビルドでデバッグすると、ブレークポイントも当たらなかったりで、ろくな事はない。
深い再帰とかが例えば演算系なら、毎回再帰前にprintfで全部の値をログに出し、
リリースビルドで動かした出力とdiffを取り、完全一致になってなければバグとして、あくまでデバッグビルドでデバッグする。
そのファイルが数GBになって、普通のdiffでは無理になったら、完全一致専用のdiffを手作りする。
実は浮動小数点だと完全一致はしないが、それなら誤差範囲を指定したスクリプトを書いて比較だ。
というのが俺流で、つまり別の戦術で回避済みだから、
リリースビルドでどんな最適化がされてても、コンパイラがバグってない限り関係ないね。
そしてコンパイラなんてバグってない。俺は誰でも書いてるようなコードしか書かないし。
それ、リリースビルドでデバッグしてる事が問題なのでは?
(まあ気持ちは分かるけども。俺も最初はそうしてて、駄目だったので上記になってる)
上下前次1-新書関写板覧索設栞歴
スレ情報 赤レス抽出 画像レス抽出 歴の未読スレ AAサムネイル
ぬこの手 ぬこTOP 0.031s