TL;DR
- コードの簡潔さと疎結合を重視するなら、メッセージパッシングが向いている。
- メモリ消費を抑えたいときや、大きなメッセージを頻繁にやり取りするときは、メモリ共有が向いている。
内容
どちらの方式を採るかを決めるときに考えるべき観点を 4 つ挙げる。
コードの簡潔さ
メッセージパッシング(チャネル)を使ったほうが、コードは簡潔になりやすい。メモリ共有(Mutex やセマフォ)ではコードが冗長になり、保護されたクリティカルセクションがあちこちに散らばりがちである。アプリケーション内のデータの流れもつかみにくい。
一方、メッセージパッシングでは、各モジュールが独立した役割を持ちやすい。各モジュールは自分の処理を並行して進めながら、必要に応じて他のモジュールとメッセージをやり取りして連携する。そのため、コードが簡潔で理解しやすくなる。入力と出力のチャネルがはっきりしているので、データの流れも追いやすい。
密結合と疎結合
メッセージパッシングを使ったほうが、疎結合になりやすい。メモリ共有では、スレッドどうしが共通のメモリ領域を通じて通信するので、各実行の境界がはっきりしない。そのため、ある実行で共有メモリの更新方法を変えると、アプリケーションの他の部分にも大きく影響する。一方、メッセージパッシングでは入出力の契約があるので、ある実行の変更が別の実行にどう影響するかを正確に把握できる。
ただし、メッセージパッシングを使えば疎結合になり、メモリ共有を使えば密結合になる、というわけではない。メッセージパッシングでは、並行実行ごとに入出力チャネルという明確な境界を定義できるので、疎結合の設計を考えやすい、という意味である。
メモリ消費の最適化
メモリ消費量を減らしたいときは、メモリ共有のほうが良い場合がある。メッセージパッシングでは、各ゴルーチンがメモリ内に自分専用の状態を持つ。あるゴルーチンから別のゴルーチンへメッセージを送ると、受け取った側は自分のメモリ内でデータを整理し、計算を行う。そのため、同じデータの複製が複数のゴルーチンに存在することがある。
通信の効率
次の場合は、メモリ共有を使ったほうが高い性能を得られる。
- メッセージが大きすぎる場合
- メッセージを頻繁に送受信する場合
メッセージパッシングでは、ゴルーチン間でメッセージのコピーを受け渡す。そのため、メッセージ内のデータをコピーするコストがかかる。このコストは、メッセージが大きいときやメッセージの数が多いときに目立つ。
キーワード
自分の理解
メッセージパッシングの「入出力の契約」とは、チャネルでいえば型や方向のことだろうか。