SSブログ

CFRunLoopSourceの使い方その2 [考え中の問題]

unixにおいてスレッドがどういう風に発展してきたか、と言うお話。昨日は20年以上前のunixがプログラミングのプラットフォームとして利用が拡大してきたころスレッドの概念ができあがったという話だった。unixはこういった実装に結びつく概念やアイデアが実験される場でもあった。RFCの思想との共通性もある。今日はその話の続き。

2.3  キャッシュメモリとプロセス

CPUは実行効率(実行速度)が優先され、メモリは逆に速度よりも容量が優先されることになり、いつしかCPUの速度がメモリの速度を大きく上回るようになってきた。

そのためCPUの近くに小さくて速いメモリを置いてメインメモリの一部をそこにコピーしてCPUはそこから命令やデータを読んだり、データを操作して書き込んだりするようになった。それをキャッシュメモリ(Cache Memory)と言う。例えば、80年代にunix ワークステーション(CADや小規模数値計算に使うための当時としては高性能のパソコン)にはMotorolaのMC68020がよく使われていたが、このCPUにはその直前の世代のCPUであるMC68010にはない、256バイトの命令キャッシュを持っていた(前にも書いたけどクリティカルなループを256バイト以下の連続する領域に詰め込むことができると効率を大きく改善できた。キャッシュの効果をプログラマとして体感することができる時代だった)。

キャッシュはメインメモリの1/10以下の容量しかないのでメインメモリの一部しか乗らない。キャッシュにないデータをCPUが読もうとするとめまずメインメモリからキャッシュに読み込む必要があるし、キャッシュの内容をCPUが書き換えた場合、そのデータをメインメモリに書き戻さなければならない。

CPUは自分の好き勝手に(コンパイラが出力した通りに)アドレスをアクセスするので、キャッシュの上にはメインメモリのいろいろなアドレス位置のデータが乗ることになる。そのためキャッシュのデータの書き換えは複雑になる。

もしプロセスが切り替わると仮想空間が切り替わる。キャッシュが仮想アドレスでマップされているとその場合、CPUからみたら同じアドレスの値でもメモリの内容は違うことになる。キャッシュもそれに従って書き換えられないといけない。キャッシュメモリの内容がどのプロセスのものか、と言う情報を持ってもいいけど、プロセスが切り替わったらキャッシュはすっぴんにクリアする、というほうがハードウェア的には簡単になる。

こうしてキャッシュの操作とプロセスとは結びつくことになった。

2.4  マルチコアとスレッド

さらにCPUの実行効率を高めるため、複数のコアをひとつのCPUの上に持つようになった(10年ほど前のIBMのPOWER4が最初らしい。比較的チップ面積が小さいのでやりやすかったのだろう)。それぞれのコアが完全に独立していればそれぞれのコアで別のプロセスが実行できる。しかし、そのときキャッシュはそれぞれに持たなければならない。逆にそうすると必ずそれぞれのコアは異なるプロセスでなければ実行できない。同じ仮想空間をキャッシュした場合、書き戻しで問題が発生するからである。

一方でコアに専用のキャッシュを持つと、これはすなわち複数のCPUがひとつのバスを共有するのと同じことになって、メインメモリとキャッシュの間の通信が増えてそこがボトルネックになる。そこでキャッシュを2段階にして、レベル1キャッシュをコアそれぞれに、レベル1キャッシュよりは大きくて遅いけどメインメモリよりは速いレベル2キャッシュを共用するのが効率的ということになった。そうなるとレベル2キャッシュはコアが単一だった場合と同じで一つの仮想空間だけを保持することになる(少なくともそうする方が実行効率は高くなる)。

こうして単一の仮想空間内の独立した実行環境としてプログラミングモデル上はスレッドが、ハードウェア上は一つのコアと対応する、というのが自然になった。なお、レベル1キャッシュは一つの仮想空間の別々のコピーになるので書き戻し問題はやはり発生する。ただし書き戻しがメインメモリに対してではなくレベル2キャッシュになってバスを経由するわけではないので、ハード的な工夫がいくつか考えだされて問題は一応解決されている。

マルチコア環境ではマルチスレッドなプログラミングが重要視される所以である。ただし、もともとスレッドは排他制御とそれによる効率低下、あるいはデバグの難しいバグの発生(僕は「発生」という言葉はバグにはふさわしくないと思っている。「仕込み」とでも言うべきである)というプログラミング上の難しさがあった。そしてスレッドはマルチコアを使い倒すためにあるのではなかった。そのためさらに微妙な問題がいくつか発生してくることになった。せっかくマルチコアを十分働かせるためにスレッドを使ったにもかかわらず、かえって性能を落としてしまう、という問題である。

2.5  ちょっと寄り道

ここまでは話を簡単にするために、単純化したところがある。例えばキャッシュメモリを仮想アドレスでアクセスするか物理アドレスにするか、そのアドレスがキャッシュ内にあるかどうかをどうやって判別するか、CPUへの命令とデータを同じキャッシュに入れるか別にするか、同じレベルに複数のキャッシュがある場合にどうやって内容の一貫性(コヒーレンシなんていう物理的な言葉が使われる)を保持するか、といったところははしょった。しかしスレッドとCPUコアとの対応を考えると、こういった詳細の影響も受ける。このへんは技術的に面白い話がいっぱいあるけど、僕はそちらの専門家ではないので突っ込まないことにする。

ひとつだけ、ちょっと話の筋道とは違うけど、キャッシュメモリにまつわる昔話。

80年代にHP(ヒューレット・パッカード)はミニコン(DEC社のVAX11に代表される中型のコンピュータ)もやっていた。結局使うことはなかったんだけど、ある数値計算専用マシンとしてそのミニコンの導入検討をしたことがある。

面白と思ったのが、そのCPUはスタック専用のキャッシュを持っていたこと。Cやpascalでプログラミングすると自動変数はスタックに置かれて、関数呼び出しもスタックを経由する。従ってスタックへのアクセス効率を上げるとプログラム全体の効率があがる、というわけである。

そのCPU(名前は忘れてしまった)には、常にスタックの先頭が入っているキャッシュがあった。キャッシュというよりほとんどレジスタと同じで、スタックの上の方をレジスタと同等に扱うことができた。つまりCなんかではアクセスの多い自動変数レジスタに置け、という指定(register指定子)ができるが、このCPUの場合自動的にそうなる、ということになる。

このスタック用キャッシュのサイズがどのくらいだったかは忘れたけど、それほど大きくはなかったので、スタックにいっぱい積まれて高くなってくると底の方はキャッシュから追い出されてしまう。しかしたいていスタックの底をアクセスするとこは少ないので、小さいサイズのキャッシュでも効率的で、しかもアドレス比較のための連想メモリも簡単で済み、有効性が高いというのである。

その当時僕はその説明を営業のにいちゃんから聞いて、頭いいなあ、と思った。

さらに面白いのは、こういった特殊なキャッシュはその後淘汰され、汎用のデータキャッシュだけが生き残った。こういうのって、アイデアは面白いんだけど、出来上がった時点でそこから先の効率改善の道はない、というところがやはりネックなんだろう。

また昔話で閑話休題。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立02/02献立02/03 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。