本書は日経Linuxで2009年6月号から2012年6月号まで連載されていた「まつもとゆきひろ 技術を斬る」に加筆・修正を加えたものです。2009年からということで古い技術もあるのでは?と思われるかもしれませんが、さすがまつもとさん!きちんと未来を見据えたテーマでかかれていて、すごく参考になりました。
本書は読み物の色がつよく、言語の過去・未来や新しい言語(Go、Dart、CoffeeScript、Lua)の言語作成者ならではの視点での解説、クラウド時代のC10K問題やスケーラビリティの話、ビッグデータ時代の NoSQL の扱い方などをまつもとさん視点で解説しています。
実際にプログラミングをやっている人であれば、ニヤニヤしながら読めることまちがいなしです。最近の技術動向をさくっと知るというのにも良いと思います。僕もとても楽しく面白く読むことが出来ました。
新人教育にいかがですか?
おぼえがき
エクストリーム未来予測
未来を予測するときの簡単なテクニック。エクストリーム・プログラミング(XP)を考案した Kent Beck が著書の中で書いている言葉。
DSL デザインの構成要素
- コンテキスト
- DSL の記述がどのような意味を持つか規定する。
- センテンス
- コンテキストの内部の記述で、関数呼び出しやメソッド呼び出しで表現される個別の動作。
- ユニット
- 単位。20.hours の hours など。
- ボキャブラリ
- 目的分野にふさわしいメソッドをどれだけ用意できるか。
- ヒエラルキ
- ネストしたコンテキスト
GC(ガベージコレクション)の3つの基本方式
マークアンドスイープ方式
先頭から生きているオブジェクトに印をつけていき、最後まで印をつけたら印の付いていないオブジェクトを回収する方式。
処理時間は、「生きているオブジェクト数」 + 「全オブジェクト数」の和(マークを付ける処理 + 回収する処理)。
コピーコレクション方式
マークアンドスイープ方式では、大量のオブジェクトが割り当てられ、そのうちごく一部だけが生き残るような場合に必要以上に時間がかかってしまう。
コピーコレクションは、マークアンドスイープ方式でいうマークを付ける処理(マークフェーズ)で、生きているオブジェクトを新しい領域にコピーするようにし、すべてのコピー(マーク)が終わったら古い領域を回収することで行う。
マークをつける処理よりもコピーする処理のほうがコストが高いため、生きているオブジェクトの数が多いと不利な方式になる。
リファレンスカウント方式
オブジェクト自身が、自分が参照されているカウント(リファレンスカウント)を知っていて、参照が増減する度にリファレンスカウントを書き換える方式。
メリットは簡単に実装ができること。
デメリットは、循環参照に対応できないこと。また、並列処理と相性がわるいこと。
応用方式
基本方式を組み合わせる形で、応用方式が多くの場合利用されている。
世代別GC、インクリメンタルGC、並列GCである。
世代別GCは、「オブジェクトのほとんどは短時間でゴミになり、長い時間生き残ったオブジェクトはより長い寿命を持つ」という性質を利用して、オブジェクトに新しい、古いのタグをつけ、新しいものだけをGCするマイナーGCと古いものも対象にするメジャーGCの2つを使い分ける方式。
インクリメンタルGCは、リアルタイム処理などで GC の中断時間を減らす目的で、GC を少しずつ処理していく方式。
並列GCは、複数 CPU を利用して GC を行う方式。
例外処理
例外が発生したときの対応には大きく2つある。ひとつは、実行を中止すること。もうひとつは、例外が発生した原因を取り除き再挑戦すること。
クロージャ
「関数オブジェクトから外側の変数がアクセス(参照や更新)できる」というのがクロージャを構成する要件の一つ。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
外側のスコープに属する変数を参照している関数オブジェクトは、そのローカル変数を「閉じ込め」る。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Ruby 1.9では、関数っぽいプログラミングを支援するために、lambda の代わりに「->」という式がつかえるようになり、また「call」メソッドを省略して「f.()」と呼び出せるようになっています。
Go
Google からリリースされた新言語「Go」は、システムプログラミング言語では初となる言語仕様のレベルで並列プログラミングを意識している。
Go の関数やメソッドは複数の値を戻り値として返すことができる。
Go には例外機構がないが、戻り値の多値をもちいて、本来の戻り値とエラーが発生したかどうかを区別できる「コンマOK」スタイルをとっている。
1 2 3 4 |
|
Go の goroutine は、メモリ空間の共有を行い、軽量であり、コンテキストスイッチが自動でマルチコアを活用できる。
ブルームフィルタ
ブルームフィルタは、あるデータが登録されているかどうかを判定できるデータ構造。
- 判定時間がデータ件数に依存しない(O(1))
- 空間効率が非常に良い
- 要素の削除ができない
- たまに間違える
ブルームフィルタは、本当は要素に入っていないけれども入っているとみなされるという疑陽性(false positive)がある。
C10K問題
OS の select コールを使わないで、epoll や kqueue などの別のAPIを利用する。または、ノンブロッキングI/O を使う。Ruby のイベントループフレームワークには、EventMachine がある。
アプリサーバ
Unicone は一般的な Master プロセスからワーカープロセスにリクエストを転送するプッシュモデルではなく、プルモデルを採用しているため。プルモデルとは、ワーカー側が処理が Master に処理を受け取りに行く方式。
key-value ストア
ACID (Atomicity:原子性、Consistency:一貫性、Isolation:独立性、Durability:持続性)特性はデータベースの世界では一般的だが、近年大規模データを扱うために分散を考えるようになってくると ACID を満たすのが厳しくなってきている。
CAP 定理というものがあり、大規模環境では、Consistency:一貫性、Availability:可用性、Partition Tolerance:分割体制 のうち同時に2つまでしか満たすことができないとされている。
大規模分散環境では、多くは Consistency を捨てている。多くの key-value ストアはトランザクション処理を持たないのはそういうこと。
アムダールの法則
一般的にマルチコアコンピュータは、CPU を複数積んでいても、その他のデバイスは共有している事が多い。たとえば、メモリやディスク、ネットワークデバイスなど。もし、CPU 以外のところに処理のボトルネックがある場合は、マルチコアを投入しても性能は改善されない。
ノンブロッキングI/O
大量の接続をさばくサーバでは、スレッドを使うとメモリ負荷やスレッド切り替えのコストが大きくなりすぎる傾向がある。そのためイベントを待ち受けてそれに対応する処理を「シングルスレッド」で処理するやり方のほうが効率的になる。
シングルスレッドで処理をするときに気を付けないといけないのは、そのスレッドが「ブロック」されると、プログラム全体が止まってしまうことである。
ブロックは、入出力待ちの場合がほとんどなため、入出力待ちを以下にブロックしないかが重要になる。