高速メモリアクセス


Level 0:まとめてアクセス
大量のメモリにアクセスする必要がある場合には、1バイトずつアクセスするよりも、4バイト(もしくは8バイト)ずつアクセスする方が格段に早くなることは明らかですね。例えば、1バイトの配列で構成されているV-RAMに画像を描く場合、次のようにします。
char	vram[width][height];
unsigned	long*	pixels;	// longは4バイトで構成されているとします
unsigned	long	carrier;
int	x, y;

pixels = (unsigned long*)vram;
for(y = 0; y < height; y++){
	for(x = 0; x < width;){
		// plot(x, y): (x, y)に描くべき色(符号なし1バイト)を返す
		carrier = plot(x++, y);
		carrier = (carrier <<8) |plot(x++, y);
		carrier = (carrier <<8) |plot(x++, y);
		carrier = (carrier <<8) |plot(x++, y);

		*(pixels++) = carrier;
	}
}
もちろん、あらかじめデータを4バイト(もしくは8バイト)で切りそろえておくことも必要です。
# 本当はアセンブラでバキバキと書くのが正解かも知れない。。
## でも、命令のスケジューリングなどを考えながらアセンブラ
## ソースを書くのはすでに人間技じゃないと思う:-P


Level1:アセンブラコードを意識した高速化(Mac/PowerPC)

 上記のコードですが、
pixels = (unsigned long*)vram;
pixels--;
for(y = 0; y < height; y++){
	for(x = 0; x < width;){
		carrierに値をセット

		*(++pixels) = carrier;
	}
}
コンパイラによっては上のように、アドレスの計算にプレインクリメントを使ったほうが高速なコードを吐き出す場合があります。ポストインクリメントを使った場合とプレインクリメントを使った場合のアセンブラコードを比較すると次のようになります。(コンパイラはMWC/C++ 1.5を使用)
ポストインクリメントを使った場合
stw      r5,0(r3)
addi     r3,r3,4
プレインクリメントを使った場合
stwu     r5,4(r3)
r5: 書き込む値
r3: 書き込まれるアドレス
stw r5, 0(r3): r3+0のアドレスにr5をワード(4バイト)として書き込む
addi r3, r3, 4: r3の値を+4してr3に書き込む
stwu r5, 4(r3): r3の値を+4した後、r3のアドレスにr5をワードとして書き込む
上のようにプレインクリメントを使ったほうが1命令だけ少なくてすむことがあります。もちろん、コンパイラによってはどちらの場合でもstwu命令を使ったコードを吐き出す場合もあります。
Level2:プロセッサキャッシュを意識した高速化(Mac/PowerPC)
…準備中…

CODE-Lab
Laboratory
H2 Home Page