Yabu.log

ITなどの雑記

第20回横浜Go読書会に参加

初参加です。

Goならわかるシステムプログラミング

Goならわかるシステムプログラミング

Goを学び始めた動機などは

yuyubu.hatenablog.com

にも書いていますが、インタプリタの本でGoのコードをちょっとみたのが大きかったかなと思います。*1

結局先日からは章末の連取問題を解くのに必死で全然読み進められていません。(p146で止まっている) 会場に向かう電車の中で軽く流し読みをして挑みました。

今回は13章のGo言語と並列処理の240ページからでした

Go1.11

Go1.11が近日発表された、ということで、リリースノートを軽く読みました。

https://golang.org/doc/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設計の極意、10%ほど読んで放置になっていたので、こちらも早いうちに読まないといけないと思います。

パタヘネとコンピュータネットワーク(セスぺの試験までには読み終わりたいがこちらもペース的に若干きつい)も平行して読みたいので、勉強することが多くて大変だなと思います。

*1:言語処理系に興味があったので直近で発売された本を片っ端から眺めています。

*2:この本には出てこないらしい。

*3:Goのメモリモデルに関係している

*4:今回の読書会で得た知見を早速使ってみました

*5:柴田さんのJava/Go研修はかなり厳しいようです