誰なのか実は知らない 私
....
↓
〈コソッとじゃないそうです、本人談によると。〉
↓
Web の世界って面白いな〜、コソっと書いてあることでも、google が引っ掛けてしまう。
さて、とあるプログラム の中で 多次元配列を使ってない
ことについて、キーワード
vertex ctmrg ising
で引っ掛かるものを見つけてしまった。ここに書いてあること、マトモである。下の4行の
メッセージも守る方が良い。でも、ちょっと残念なのが「一番実行時間のかかるルーチンで
キャッシュ効率の良いメモリーアクセスを発生させる」ことを考えてたのだけど、一番実行
時間のかからないルーチンについて言及されている所。まあ、サブルーチン側では行列表現
しなさい、というのは良いかもね、行列演算を検出するコンパイラに運良く当たれば高速に
処理してくれるから。
で、その、メモリーアクセスの問題だけど、例えば A(L,k,j,i) という行列があって、添字
がそれぞれ NL, Nk, Nj, Ni だけ回るとしよう。素直にループを回すなら
do i = 0, Ni-1; do j = 0, Nj-1; do k = 0, Nk-1; do l = 0, NL-1
と do ループを並べることになる。が.... Ni = 10000, Nj = Nk = NL = 2 だと? 最近のコンパ
イラは用心深く「内側以外のループが最長である場合も考えてコード生成する」ので、たぶ
ん i のループを連続して回して、8 変数おきのメモリー・アクセスを実行させるようになる
だろう。これがイヤだったら、予めどこかで添字の入れ替えを行なう目的で
B(i,L,k,j) = A(L,k,j,i)
という代入を書いておかなければならない。もう少し複雑なパターンもあって、
C(j,k,x,d,e,f) = C(j,k,x,d,e,f) + A(i,j,k,L,x,y) B(d,i,e,L,f,y)
なんて演算が現れたとしよう。できれば、こんな難儀な物は紙とエンピツで落としておきた
いのだけど、多次元配列を「実質的には複数の足をまとめた行列として扱う。また、行列と
しての足のまとめ方は場所によって変化する」という使い方が避けられない場合も実際には
良く出くわすのだ。上の例では、まず A と B を 行列 (〜2 次元配列) 化しないといけない。
A(i,j,k,L,x,y) → A'(i,k,x, j,L,y) B(d,i,e,L,f,y) → B'(d,e,f, i,L,y)
この下準備をしておけば、C を「行列」 A' と B' の縮役で簡単に得ることができる。まあ、
B' の足の順番をどうするかは好みの問題で、逆にしておけば C = A'B' とか C = B'A' など、
標準的な書き方もできる。こういう変換がボコボコ出て来ると、配列変換の為にいちいち
do ループを切るのがイヤになって来るというのが本音。で、どう「解決」したかというと、
1 次元配列だけを使って、必要に応じて次のような「上位・下位入れ替えルーチン」を複数
回呼ぶことにした。
cccccc subroutine exchg2( m1, m2, A, B ) integer i, j, m1, m2; real(8) A(0:*), B(0:*) do i = 0, m1-1 do j = 0, m2-1; B(j*m1+i) = A(i*m2+j) end do; end do; end cccccc subroutine exchg3( m1, m2, m3, A, B ) integer i, j, m1, m2, m3; real(8) A(0:*), B(0:*) do i = 0, m1-1 do j = 0, m2-1; B((j*m1+i)*m3:(j*m1+i)*m3 + m3-1) & = A((i*m2+j)*m3:(i*m2+j)*m3 + m3-1) end do; end do; end アセンブラでは、良くお目にかかる処理ですよね。もちろん、コレを使ってしまうと、書い た本人しかわからないプログラムになる。わっはっは〜?! .... というパイプライン的発想は、最近では流行らないかな、みんなパラパラだもんネ。