初参加です。
- 作者: 渋川よしき
- 出版社/メーカー: Lambda Note
- 発売日: 2017/10/19
- メディア: テキスト
- この商品を含むブログを見る
Goを学び始めた動機などは
にも書いていますが、インタプリタの本でGoのコードをちょっとみたのが大きかったかなと思います。*1
結局先日からは章末の連取問題を解くのに必死で全然読み進められていません。(p146で止まっている) 会場に向かう電車の中で軽く流し読みをして挑みました。
今回は13章のGo言語と並列処理の240ページからでした
Go1.11
Go1.11が近日発表された、ということで、リリースノートを軽く読みました。
Webassemblyサポート
サンプルを試したところ、ChromeのコンソールにHello Worldが出せたらしい
Goのデバッガ
最適化したコードでもデバッグ情報がかなり出るようになった
Go fmtが少々変わる
古いバージョンのGoで静的解析が通っていたソースが 今回のバージョンでフォーマットの違いで警告が出る恐れがある
- CIのバージョンが違うと怒られる可能性がある
Go2
まだ仕様は出ていないが、軽くディスカッションされている
- generics
- エラー処理に関して重要な変更が2件
Go 1.12は半年後くらい。 いつ出るかわからない
mutexはgorutin間で通信する時に重要
書き込みが1つだけであっても、 他のスレッド(gorutin)から「書いた」値を読めるように書き込みの完了までをロックする必要がある。 *2*3
メモリモデルは
https://golang.org/ref/mem#tmp_5
が詳しい
重い処理をマルチスレッドで実行する場合はCPUの物理的な個数分のプロセスに分解するとパフォーマンスが良い
Goではruntime.NumCPU()
でCPUの個数を取得できるが、こちらで確認できるのは物理CPUの数ではなく、
論理CPU の数であるため注いが必要。
本書に記載されている通り、SMT(Simultaneous Multi-Threading)機能などにより、CPUのコア数が多く評価されることがある。
自分の環境で試したところ、デュアルコアであるにも関わらず、4が帰ってきた。
Goでは並列を保証しない。
平行だけ。これは各CPUに割り当てるプロセス「プロセス」は作れても 空いてる新規にプロセスを割り当てるCPUを増やすなどの動的な制御をGo内だけで処理できない。
実行じにGOMAXPROCの環境変数で実行時に指定するか、OSのスケジューラによる割り当てに任せる。
p242で配列が1000万件あったら,という話題になった
package main import ( "fmt" "time" ) func main() { tasks := []string{ "cmake ..", "cmake. --build Release", "cpack", } for _, task := range tasks { go func() { fmt.Println(task) }() } time.Sleep(time.Second) }
- 上記のコードは動機をとっていないので最初のgoroutineの実行時にfor文が周りきった状態になり最後の要素が3回出力される、という悪い見本コードです.
- ちなみに動機を
Sleep
を使って取るのもあまり良い手本ではありません
- ちなみに動機を
- 流石に1万もあれば最後ではなく、最初の要素が出るでしょう、という話になった
- 結局はスケジューラーによるのでは?
実験してみました。まずはサンプルコードのまま3件でそのまま動かしました。
cpack cpack cpack
本書の記載通り、最後の要素であるcpack
が帰ってきました
ではsliceの中身を100個に増やしてみましょう
package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup wg.Add(100) tasks := []string{ "1", "2", "3", "4", "5", "6", ...(略) "99", "100", } for _, task := range tasks { go func() { fmt.Println(task) wg.Done() }() } wg.Wait() }
このコードで実験してみました
注:前のコードでmain終了を待たせる同期を取っている箇所、time.Sleep(time.Second)の期限で処理が完了しなかったのでWaitGroupを使っています*4
結果
13 6 30 31 41 10 54 54 54 54 54 54 54 54 46 54 54 54 54 62 68 68 68 68 68 68 68 68 78 78 60 62 68 68 68 54 68 70 78 68 78 78 78 78 78 62 78 78 78 78 78 100 100 100 100 78 100 100 78 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 100 73 100
という出力が得られました。
一番最初/最後の要素が必ず出るという訳ではなさそうです。 結局は内部でどのようにスケジューリングしているか、という話なのでしょうか。
sync/atomicパッケージはgoで書かれていない
このあたりのコードは全てアセンブリで描かれている。
CPUごとに処理をさせたいときはCounting semaphoreを使う
Wikipediaによると、
任意個の資源を扱うセマフォをカウンティングセマフォ、値が0と1に制限されている(ロック/アンロック、使用可能/使用不可の意味がある)セマフォをバイナリセマフォと呼ぶ。後者はミューテックスと同等の機能を持つ。
とのことです
https://ja.wikipedia.org/wiki/セマフォ
セマフォは大学の時にOSの単元で軽く学習しましたが、 あまり覚えておらず、他の分散系の読書会でも出てきたので、どこかで復習しておきたいと思います。
interface{}はVariant型
読んでいるコードのところでわからない箇所があって助け舟を出してもらったりしました。
var count int pool := sync.Pool{ New: func() interface{} { count++ return fmt.Sprintf("created:%d", count) }, } pool.Put("manualy added: 1") pool.Put("manualy added: 2") fmt.Println(pool.Get())//manualy added: 1 fmt.Println(pool.Get())//manualy added: 2 fmt.Println(pool.Get())//created:1
poolに中身がないとNew:~
以降が実行されてcountがインクリメントされた後string型が返されます。
interface{}の部分をStringに変えたり色々試してみましたが、まだこの部分の文法が何をやっているのかよくわかりませんでした。
この辺は時間をかけてGoを学んで吸収したいと思います
感想
Javaの有名な本を沢山訳されていたり、大きな講演会の基調講演をされている柴田さんがやられている勉強会なので、 前から気になっていましたが、敷居が高いかなと思い参加せずにいましたが、この読書会はそこまでハードコアではないと耳にしたので*5、思い切って参加してみました。
今回の範囲ですが、GoはランタイムがOSのような資源管理をしているのが面白いと思いました。
柴田さんが懇親会で「若い人はまともなAPI設計ができない、経験が積めていない」という趣旨のことをなんども仰られていました。
APIデザインの極意 Java/NetBeansアーキテクト探究ノート
- 作者: Jaroslav Tulach
- 出版社/メーカー: インプレス
- 発売日: 2014/07/02
- メディア: Kindle版
- この商品を含むブログ (8件) を見る
柴田さんが訳されたAPI設計の極意、10%ほど読んで放置になっていたので、こちらも早いうちに読まないといけないと思います。
パタヘネとコンピュータネットワーク(セスぺの試験までには読み終わりたいがこちらもペース的に若干きつい)も平行して読みたいので、勉強することが多くて大変だなと思います。