概要
現在、Go言語のgoroutineの仕組みについて学習しているのですが、その際の前提知識となってくる
- 並行・並列
- マルチプロセス・マルチスレッド
- マルチコア・マルチプロセッサ
上記のコンピュータの基礎知識についてまとめてみました。
この記事や参考文献について理解した後に、goroutineなどの学習をするとスムーズに理解できるかと思います。
スレッドとプロセス
実行中のプログラムは『プロセス』と呼ばれ、プロセスは 1 つ以上の『スレッド』を持ちます。
$ ps aux
でシステム上の全プロセスを確認できます。(Mac OS Intelコア)
$ ps aux
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
...
kiyo 497 42.0 0.0 33924816 13080 ?? S 4Dec23 508:22.62 /usr/libexec/siriknowledged
kiyo 527 18.3 0.4 35910460 145248 ?? R 4Dec23 38:10.16 /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
...
kiyo 9833 7.3 8.1 45880000 2730928 ?? R Sat10PM 101:25.18 /Users/kiyo/Applications/GoLand.app/Contents/MacOS/goland
root 136 5.1 0.0 33859108 2580 ?? Ss 4Dec23 55:54.11 /usr/sbin/notifyd
_trustd 10949 4.8 0.0 33911508 8936 ?? Ss 8Dec23 66:21.91 /usr/libexec/trustd
root 358 3.4 0.0 33859596 3904 ?? Ss 4Dec23 148:24.03 /usr/libexec/sysmond
kiyo 501 3.3 0.0 33899892 7016 ?? S 4Dec23 27:15.83 /usr/libexec/MTLAssetUpgraderD
root 1 2.9 0.1 35238412 19236 ?? Ss 4Dec23 135:19.80 /sbin/launchd
...
1 つのプロセスには、OSによって1 つの独立したメモリ空間 (メモリ領域) が割り当てられます。(メモリ空間はプロセスから OS に要求すれば(空きがあれば)増やせます)
プロセスのメモリは自身のものしか参照できないため、基本的にお互いのメモリへアクセスすることはできません。
コンピュータにおいて複数のプロセス(タスク)を切り替えて実行できるシステムのことをマルチプロセス(マルチタスク)と言います。
後述するCPUコアがひとつの場合、同時実行できるプロセスやスレッドはひとつだけですが、マルチコアの場合複数のプロセスとスレッドを同時実行(並列処理)できます。マルチプロセスは、OSの機能によりプロセス間通信を行うため、オーバーヘッドが発生します。
ことプログラミングにおいて、プロセスを意識する必要はあまりないです。
一方で、スレッドは、そのスレッドが所属するプロセスのメモリ空間を共有するので、スレッド同士でグローバル変数などの共有リソースへアクセスすることができます。
また、ひとつのプロセスで複数のスレッドが動くことをマルチスレッドと言います。
マルチスレッドプログラミングが難しいと言われる所以が、上述したメモリ空間を共有するという点にあります。複数のスレッドが並行的に実行されるコードを何も考えずに書くと、データ競合、デッドロック、ライブロック、リソース枯渇などの問題を引き起こします。
これらの問題を引き起こさないように、一度に1つのスレッドのみがその共有データにアクセスするようにアプリケーションでしっかり排他制御をしなければなりません。
複数のスレッドが並行的に実行されても安全な状態のことを、スレッドセーフであると言います。(Go言語のgoroutineの文脈だとゴルーチンセーフといった言い方をします)
プロセッサ
プロセッサ(Processor)は、コンピューターやその他のデジタルデバイスにおいて、命令を解釈し、実行する主要な電子回路です。一般的に「CPU(Central Processing Unit:中央処理装置)」とも呼ばれ、コンピュータのプログラムを解釈する「頭脳」とも言えます。
プロセッサがどのようにプログラムを解釈し処理しているかは、下記サイトがわかりやすいかったので是非参考にしてください!
https://itmanabi.com/cpu-order/
マルチプロセッサとマルチコアプロセッサ
処理性能を高めるために1つのコンピュータに複数のプロセッサを搭載したものをマルチプロセッサと呼びます。
1990年代の前半までは、1つの半導体チップに1つのプロセッサしか搭載できなかったので、マルチプロセッサシステムは必然的に複数のプロセッサシステムを利用するシステムでした。
しかし、ムーアの法則により半導体チップに集積できるトランジスタ数が増えたことで、複数のプロセッサを1つの半導体チップに搭載できるようになりました。このそれぞれのプロセッサ部分をコア(CPUコア)と呼びます。そして、コアを複数もつプロセッサをマルチコアプロセッサと呼びます。
スレッドがCPUコアに命令を与えるため、SMT(同時マルチスレッディング)登場以前は、CPUコア数 = 同時実行できるスレッド数でした。
しかし、SMTの登場により1つのコアに対して複数のスレッド(多くは2つのスレッド)を割り当てることができるようになりました。(この技術によってスレッドへ割り当てることができる理論上のコア数を論理コア数や仮想コア数と言います)
物理的に1つのコアを、OSからは2つのコアであるように見せかけることができ、コアの利用率を上げることができるようになっています。
例えば、IntelのXeon 7500チップは8個のコアを持ちます。
これ以外にもこれらのコア同士で共有する3次キャッシュやメモリコントローラ、外部インターフェース(QPI)なども1つの半導体へ集積しています。
マルチコアプロセッサの構造
マルチコアプロセッサの場合、コアは複数存在し、3次キャッシュ以下のメモリコントローラや外部インターフェースースやそれらの間をつなぐスイッチは全コア間で共有するという構造になっています。
コア部分は、
- 命令フェッチから実行パイプラインまでのユニット郡
- 1次命令キャッシュ
- 1次データキャッシュ
- TLB
また、上記以外にも下記図のように、「1コアに2次キャッシュまで持たせてメインメモリのみを共有する」というマルチコアプロセッサもあります。さまざまな構造のバリエーションがあります。
以上のようにマルチコアプロセッサには1次キャッシュや実行ユニットは独立しておりコアごとに存在します。そのため、マルチスレッドプロセッサ1のように資源を複数のスレッドで取りあうということがなく、各スレッドの実行性能が他のスレッドの影響を受けにくいという特徴があります。
一方で、コアをマルチスレッド化したところで数%しかコアの面積は増えませんが、2コア化すれば確実にプロセッサ部分には2倍の面積が必要となります。
並行と並列
この話で大事なのは、「どの文脈で話をしているのか」ということです。
「時間軸」の文脈なのか、それとも「プログラミングのコードの状態とランタイム」の話なのかによって「並行」と「並列」の定義は変わってくる*ので、ここについて議論するときはどの文脈で話をしているのかということを汲み取る必要があると私は考えています。
こちらについては、さき(H.Saki)さんの こちらの書籍が非常にわかりやすくまとまっていたため、記事へのリンクのみとさせていただきます…!
参考
- 【図解】CPUのコアとスレッドとプロセスの違い,コンテキストスイッチ,マルチスレッディングについて
- 今さら聞けないマルチプロセッサの基礎教えます ――キャッシュの共有,割り込みの共有,OSによる制御
- マルチスレッドと並行処理をわかりやすく説明します
- Wiki-マルチタスク
- Wiki-スレッドセーフ
- プロセッサを支える技術
- Go言語による並行処理
-
複数のスレッドを並列に実行できるハードウェア機能を持ったプロセッサを「マルチスレッドプロセッサ」といいます。また、ここでいうマルチスレッドは、「複数のスレッドを実行できる」というハードウェアの実行能力のことを指しており、この記事で言及しているソフトウェアのマルチスレッドとは異なる概念です。 ↩