例えば3つのメンバを持つクラス
class hogeclass{
int value1;
int value2;
int value3;
}
があったとして、
void hoge(class hogeclass *a,class hogeclass *b){
a->value1だけ使って計算
b->value2に結果を格納
}
ような関数は止めようという話。
ずっと前に仕事でこんなコードを見て、
読めねー!っと世界の中心で叫んだので。

一番の問題は、入力と出力の関係の明確化なんだけど、
パフォーマンスでも微妙だよと言いたい今日この頃なのです。

入力の出力の関係の明確化というのは、
その関数が何を入力として受け取って、何を出力とするのかを
はっきりしておかないと、わけがわからなくなるという話。
この場合だと、関数hogeが呼ばれた時点で、
aの全てのメンバを計算に使う可能性も、
aの全てのメンバが書き換わる可能性もあるわけで、
例えば、a->value2がばぐった時とかに、どの関数に影響が起こるかわからない。

という事でこれは、
int hoge(int value1){
return value1で計算
}
と定義して、
b->value2=hoge(a->value1)
とするのが望ましい。
前回のプログラムだと、その関数が何をするのか分からなかったけど、
この関数だとぱっと見ただけで、
何か線形な計算をしそうだと理解できる。
もちろん、グローバル変数は極力使わないべきだ。

で、まぁ、ここまではよくある話なんだけど、
classへのポインタで渡しちゃうとパフォーマンスまで悪くなるんじゃね?
というのが今日のエントリ。

というのも、キャッシュが問題なんですよ。
引数に値を渡した場合は、引数はスタックに積まれて、
さらにローカル変数も連続的にスタックに積まれる、
つまり連続したメモリ空間におかれるんだけど、
クラスへのポインタを使っちゃうと、
クラスの実体がメインメモリのどっかにあるので、
ローカル変数とはかけ離れたメモリを参照する事になってしまうんですよ。

それの何が問題かというと、1次キャッシュは、
ダイレクトマップって言って、物理アドレスに対応して、
キャッシュの場所が一意に決まっちゃう。
つまり、
 キャッシュ上のアドレス=物理アドレス%100
とかってこと。
ここで、連続的にスタックに詰まれたローカル変数は、
物理アドレスがインクリメンタルされていくだけなので、
キャッシュ以下のサイズであれば全部キャッシュに乗るけど、
クラスへのポインタを使っちゃうと、
クラスの実体の物理アドレスを参照するから、
ローカル変数の物理アドレスとぶつかっちゃうんです。

つまり、ローカル変数のアドレスが1,2,3だったら
キャッシュのアドレスは1,2,3でぶつからないけど、
ローカル変数1,2とクラスの実体101だったら、
キャッシュのアドレス1,2,1で、1でぶつかる。
レジスタへは、キャッシュを通してしか転送できなくて、
ローカル変数1とクラスメンバは同じキャッシュの場所を使うから、
ローカル変数1を使った後にクラスメンバを使ったりする度に、
キャッシュの内容の入れ替えが発生して、
キャッシュ<>2次キャッシュや、
キャッシュ<>メインメモリのアクセスが発生するわけ。
ここで、キャッシュは非同期RAMで1CLKで値が取れるけど、
2次キャッシュは数倍はかかる。
メインメモリになると数十倍ですよ。

入力と出力を最小化しない設計は、速度的にもヤバイ!
という事で、クリティカルパスでこんなことをやっちゃわないようにしよー。

-----------------------------------------------------------------
2008/3/2追記

まぁ実際は同じキャッシュラインに乗る確率はかなり低いので、
速度については気にしなくていいと思う。
入力と出力の数を最小にして、読みやすいプログラムにするのがベストですね。