#godocreading 第11回
よむもの
今回の録画
前回のおさらい
Goのデザイン思想
- よくあるプログラムのミスにたいするよく定義されたセマンティクスを持っているということも、Goにとって重要なポイントだ
- Hoare シンプルに使えることと同じように、ソフトウェアプログラムは間違って使うのが難しくなければいけない。プログラミングの間違いに対して親切でなければいけない、間違いには明確な知らせがあるべきだし、間違いの効果が予測不可能であってはならない
- バグのあるプログラムにもよく定義されたセマンティクスがあるべきだというコモンセンスは、思うほど一般的に浸透しているものではない。C/C++では、未定義動作がある種の自由裁量になっていた。どういう自由裁量かというと、わずかにバグったプログラムを、ぜんぜん違う形でバグったプログラムに変換する自由だ
デザイン思想をのべたあと、旧メモリモデルは同期演算の説明をしているが、その中にsync/atomic
やsync
の新しいAPIについては言及がない。
また、正しくない同期の例はあったが、正しくないコンパイル(最適化)の例はなかった
C/C++11のモデルを採用するように勧めてきた人々もいたが、それは我々にはリスキーにみえた。なので、より保守的なアプローチをとることにした。(過去の研究でJavaやC/C++のメモリモデルが直面した難しい微妙な問題をしっているので)
コンパイラ作者とプログラマをガイドするに十分なメモリモデルを与えるのは重要だが、それを正しくかつフォーマルに行うのは最も才能ある研究者の手にさえ届かない課題だ。
Goについては役に立つために最低限必要なものを言い続けるだけで十分だろう。
Document Go's overall approach
don't be cleverは重要だけどそれだけだと十分じゃない
happens-beforeの詳細にはいるまえにもっとGoの全体的アプローチについて書く必要があると思う
私はGoのアプローチについての間違った要約を何度も目にしてきた。たとえばGoはC/C++と同じようにDRF-SC or Catch-Fireであるというようなものだ。この誤読は理解できる。ドキュメントはDRF-SCアプローチをはっきり書いてないから。そしてドキュメントは短いし書かれている問題は微妙で難しい問題なので、読者は実際に書いてあること(または書いてないこと)ではなく自分が予め予想していた(※思い込んでいた)内容を読み取ってしまいがちだ。
なので次のようなものをつけくわえたい
Overview
Goのメモリモデルは言語の他の部分と同様に、意味論をシンプルで理解しやすく役に立つことを目指しているということ。
data raceは、あるメモリ位置へのwriteが、同一位置への他のread/writeと平行におこることだ。(ただし、それらのアクセスがsync/atomicで提供されるatomicデータアクセスではない場合)
すでにのべたように、プログラマはデータレースを防ぐことが強く推奨される。データレースがなければ、Goのプログラムはすべてのごルーチンが1つのプロセッサに多重化されてじっこうされたかのようにふるまう。この性質はしばしばDRF-SCとよばれる。データレースフリーなプログラムは、逐次一貫的なやりかたでふるまう。
他のプログラム言語は、データレースを含むプログラムについて2つのうちの1つのアプローチをとるのが典型的だ。C, C++に見られるアプローチは、データレースのあるプログラムは無効であるとするものだ。この場合コンパイラは任意の驚くべき方法で壊れることができる。2つ目のアプローチは、Java/JavaScriptにみられるアプローチは、データレースのあるプログラムにも定義された意味論があり、競合の(もたらす)可能なインパクトを制限し、プログラムをより信頼できてデバッグしやすくするものだ。
Goのアプローチは中間にある。data raceのあるプログラムはある意味で無効である。つまり、(言語処理系の)実装はデータレースを報告してプログラムを終了してよい。しかし、そうしない場合には、データレースのあるプログラムは定義されたセマンティクスをもち、それにより間違ったプログラムがより信頼できてデバッグしやすいものになる。
※補足 DRF-SCとDRF-SC or Catch Fire
"DRF-SC"と"DRF-SC or Catch Fire"はそれぞれメモリモデルの設計指針・アプローチを表すことばです
- DRF-SCアプローチ
- data-raceのないプログラムは逐次一貫的に振る舞うことを保証する。
- data-raceのあるプログラムは?→有限通りの定義された振る舞いをする
- DRF-SC or Catch Fireアプローチ
- data-raceのないプログラムは逐次一貫的に振る舞うことを保証する。
- data-raceのあるプログラムは?→何が起きるかわからない。未定義動作の一種として取り扱われる。
Happens Before sectionの終わりで、ある種のレースがデータの破壊につながることを明確にすべきだ。今はこれで終わっている:
単一マシンワードよりも大きなサイズのreads/writesは、複数のマシンワードサイズの演算としてふるまい、その順序は特定されない
これも付け加えるべきだ:
これはマルチワードデータ構造における競合は一貫性のない値を生じる可能性があるということだ。(つまり単一の書き込みの結果として説明できる結果にならないことがある)
値が内部の(pointer,length)のペアや(pointer, type)のペアの一貫性に依存する場合、そのような競合は任意のメモリ崩壊につながりうる。(interface値や、map, slice, stringsの場合がそうだ)
Document happens-before for sync libraries
すばらしいぶろぐ
自分でもsliceの崩壊を再現してみた。