大きな領域をクリアする

大量の領域をCPUのみでクリアする方法として、

1. memset
2. 32bit単位で書いていく方法
3. 32bit単位で書いて、それをmemcpyしていく。

があると思う。他にないかな。


1. memset

void *memset( void *dest, int c, size_t n );

アドレスdestからnバイト分、cで埋める。
cはint型だから通常4byteと思いきや、最下位1byteをコピーする。
仕様なのか実装依存なのか不明。

例:c=0x12345678とすると、0x78をひたすらコピーする。

したがって、0x12345678でクリアしたくても出来ない。
メモリ上の値は以下のようになってしまう。
78 78 78 78 78 78 78 78 ...
こういうのはできない。
78 56 34 12 78 56 34 12 ...

長所:高速
短所:多バイトの値でクリアしたい場合に使えない。


2. 32bit単位で書いていく方法

現在のCPUは大抵32bit単位でアクセスできるので、(最近は64か?)
その単位でメモリを埋めていく方法。

 for(i = 0;i < SIZE;i++) *(dest + i) = 0x12345678;

長所:実装が簡単で短い。32bit単位で任意の値を設定できる。
短所:低速


3. 32bit単位で書いて、それをmemcpyしていく。

 // 1番目の領域
 for(i = 0; i < AREA_1;i++) *(dest + i) = 0x12345678;
 // 残りの領域
 for(i = AREA_1; i < AREA_LAST; i += AREA_1)
   memcpy(dest + i, dest, AREA_1);

長所:やや高速で、多バイトの値でのクリアにも対応。
短所:AREAのサイズを決める基準が難しい。


というわけで、速さの順に並べると

memset > dword + memcpy > dword

となる。



memsetとかmemcpyが内部で何をやっているのかは、Cの実装次第ですが、
こんな感じ。ARMを想定。

memset:
・クリアする値にマスクして最下位1byteのみ抜き出す。(0x12345678→0x00000078)
・1byteを4byteに膨らまし(0x00000078→0x78787878)
・さらに、複数のレジスタにその値をコピーする。(r1=r2=0x78787878)
・値をセットしたい領域分ループして、そのレジスタ値をメモリにセットする。
レジスタを使用する量が多いほど、ループ量が少ない。
レジスタのビット数が多いほど、ループ量が少ない。

memcpy:
・コピーするサイズに応じて、使用するレジスタ数を決定
→出来るだけ多く使った方が効率がいい。
・複数のレジスタへコピー元から値を読み込む
・複数のレジスタからコピー先へ値を吐き出す。

ここで、複数のレジスタを用いるので、32bit単位でのクリアよりも
効率が良いってことなのだと思う。当たり前のことですが、メモ。

参考)
複数のレジスタの値を同時にメモリに吐き出す命令(STM命令)。
複数のメモリの値を同時にレジスタに読み込む命令(LDM命令)。


領域のクリアをする場合に、コンパイラが最適化して変な動きに
なるかもしれないので、(memsetが消えてしまうとか)
そうなると困るようなポインタはvolatile宣言しておいた方が良さそう。
ドライバ屋さんはそうしているのでしょうけれども。