Cベース設計 3分間クッキング(4) ――メモリ・アクセスのコスト その2
春は木の芽(山椒の若葉)が旬だ.この季節になると,以前料理屋さんで食べた木の芽をどっさりとかけた煮物の香りを思い出す.スーパの野菜売り場では,導電スポンジの上に並べたLSIのごとく,水をしみこませたスポンジの上に木の芽をていねいに並べて売っている.5枚で150円.これはそんなにたくさん使わない場合の価格設定.あの煮物をまねして作ってみたいのだが,そのためには木の芽を山に摘みに行かねばならない.
山椒はたまに庭に生えることがある.どこからか鳥が種を持ってきてくれるらしい.しかし,山野を好むのか,庭に生えたものは根が浅く,あまり強くない.また,アゲハチョウに見つかると丸坊主になって一巻の終わり.それを避けるには,山椒の幼木を見つけたときに,夏みかんなどのアゲハチョウが好むほかの木の近くに植え替えてやるとよい.そうするとアゲハチョウは夏みかんに気をとられて発見率が減るらしく,ほったらかしていても山椒は長生きになる.
ウナギの蒲焼に欠かせない山椒の粉は,秋に熟して赤くなった実を乾燥させ,果皮をすりこぎですりつぶして作る.山椒には雄の木と雌の木があり,実がなるのは雌の木.(*)が山椒の実だとすると,果皮は()の部分で,そこが山椒の粉になる.
* * *
今回は,アルゴリズム記述のメモリ・アクセスに起因する消費電力などのコスト評価の続きとして,ポインタ・アクセスに使用される間接参照演算子*のオーバ・ロードを紹介する.
リスト1の24行目のoperator *()が*演算子のオーバ・ロードで,ここから3行目のbarを呼んでいる.今回のbarは必要最小限で,intの参照と書き込みのアクセス計数だけを行っている.また,ポインタ・アクセスの場合,*(p+3)などのアドレス演算も準備する必要がある.15,16行目がアドレス演算で,アクセス単位のバイト・サイズSをテンプレートで受け取り,バイト・アドレス単位で増減するようにした.17~22行目は主なアドレスの増減演算.*this+1の場合は15行目が呼ばれ,Sが4の場合はアドレスが4増える.
28行目が3×3の2次元配列を想定した宣言で,列挙型でアドレスを初期化している.31行目は2次元配列の要素のアドレスを&演算子でmat2に渡す.&は6行目でオーバ・ロードされていて,アドレス(24)をもったfooが返される.fooo?とbarが相互関係になっているため,1行目がfooの前方参照.35行目がメモリ・アクセス回数の表示で,このサンプルでは読み出しが3回,書き込みが12 回と表示される.
山椒の粉は果皮をすりつぶしたものなのに,最初,まちがえて種ごとすりつぶしたことがある.味見をしたら,9Vの電池をなめたときのようだった.*()のように,ちゃんと果皮から山椒の種を出さなくてはならない.
〔リスト1〕間接参照演算子のオーバ・ロード
1 template <int S> struct foo;
2 int R_CNT,W_CNT;
3 template <int S> struct bar {
4 int ad;
5 bar(int ad_i):ad(ad_i){}
6 foo<S> operator&() {return foo<S>(ad);}
7 operator int() {::R_CNT++;return 0;}
8 int operator=(int d){::W_CNT++;return d;}
9 };
10 template <int S> struct foo {
11 int bs;
12 foo(int bs_i=0):bs(bs_i){}
13 operator int() {return bs;}
14 int operator=(int i) {bs=i;return i;}
15 foo operator+(int i) {return foo<S>(bs+S*i);}
16 foo operator-(int i) {return foo<S>(bs-S*i);}
17 foo operator++() {*this=*this+1;return *this;}
18 foo operator--() {*this=*this-1;return *this;}
19 foo operator++(int){*this=*this+1;return *this- 1;}
20 foo operator--(int){*this=*this-1;return *this+1;}
21 foo operator+=(int d){*this=*this+d;return *this;}
22 foo operator-=(int d){*this=*this-d;return *this;}
23 bar<S> operator[](int i){return bar<S>(bs+S*i);}
24 bar<S> operator *() {return bar<S>(bs);}
25 };
26 #include<iostream>
27 int main(){
28 foo<4> mat1[3]={0,12,24};
29 ::R_CNT=::W_CNT=0;
30 for(int x=0;x<9;x++) mat1[x/3][x%3]=0;
31 foo<4> mat2 = &mat1[2][0];
32 for(int x=0;x<3;x++) *(mat2+x) = x;
33 int tmp;
34 for(int x=0;x<3;x++) tmp = *mat2++;
35 std::cout<< ::R_CNT<<" "<< ::W_CNT<<std::endl;
36 }
(本コラムはDESIGN WAVE MAGAZINE 2004年5月号に掲載されました)
◆筆者プロフィール◆
さめしま・まさひろ.高速ディジタル回路のコンサルティングやEDA ツール関係のしごとに従事.