Part 16.メモリレイアウト(9)
■ 論理的なクリアの手法
・可変長配列
可変長配列の場合、配列のサイズ(どこまで使用しているのか)をどこかに格納しておき、そのサイズを0にすることにより論理的なクリアが出来る。さきほどから何度も出てきている文字列のクリアも同様である。
このとき配列のサイズを格納してある場所はどこだろう?
Pascal風文字列として、配列の先頭に長さを格納しても良いが、もしその場合であっても、コード上は
class String
{
ushort Length;
UCS2 Body[31];
}
のように長さを表すfieldが分離しているほうが良いだろう。また、文字列の結合などをしないなら、クリアされているかクリアされていないかだけの1bitのフラグだけ提供できれば十分かも知れない。
class String
{
bool isClear;
UCS2 Body[31];
}
もしこのときString配列が1024個欲しくなった場合、どうなるだろう?
String[] strings = new String[1024];
(この言語が何かを問わないで欲しい。脳内でC++なりC#なりJavaなりの表記に置き換えるべし)
こうして作られたString配列のisClearは“集積”されたほうが良いことがある。つまり、以下のクラスのfield順にメモリにレイアウトされたほうが好ましいことがある。
class Strings
{
bool isClear[1024]; // 1024bits = 128bytes
UCS2 Body[1024][31];
}
なぜなら、すべての配列をクリアするということは頻繁に起こりうるし、そのときに128バイトをクリアするだけで済むし、クリアされているかどうかを頻繁にチェックするような使い方(例えば、hashの実装)をしようと思ったときに、isClearを頻繁にチェックすることになるが、これが一箇所に固まっていればCPU cacheに乗りやすくなるからである。
このようにある構造体ないしクラスを配列として確保したときにメモリレイアウトを上のように調整したいことがあるが、ここまで細かくメモリレイアウトを指定できるメジャーなプログラミング言語は無いと思う。そこで、C++なんかで書くときは、
class Strings
{
unsigned char isClear[1024/8]; // 1024bits = 128bytes
UCS2 Body[1024][31];
}
などと直接的に配列になったときのclassを定義する。これではプログラマの思考がストレートに表現出来ているとは言い難い。プログラマの希望は、単にisClearとBody[31]というfieldを持つ構造体を配列化したいだけである。しかし上のclass定義は、メモリレイアウトの調整をプログラマ側の負担としているばかりでなく、その結果、非常に読みにくいコードとなっている。
(つづく)