NArrayに多次元配列でアクセスする方法
仕事でRuby NArrayライブラリを使って、配列のアクセスを高速化していたのですが、
どうしてもLoopを回す必要があったので、そこをC化しています。
http://d.hatena.ne.jp/yukichanko/20091026/1256573572
NArrayは多次元配列を定義しても、実際には全て1次元配列に展開してしまうので、
Cからアクセスするときも、下記のようなマクロを作り、1次元配列でアクセスしていました。
#define ARY2(var, i, j, nx, val) var[(j) * (nx) + (i)] = (val) #define ARY3(var, i, j, k, nx, ny, val) \ var[(k)*(ny)*(nx) + (j)*(nx) + (i)] = (val)
このやり方でもよかったのですが、C側のコードをNArray用にカスタマイズする必要があるので、いまいちうまくありません。
ということで、ラッパを一層追加することにしました。
注意しないといけないのは、NArrayのライブラリは、
a = NArray.float(5, 2)
という定義をしたときに、Xが内側、Yが外側の配列になる、ということ。
図で書くと、
□□□□□■■■■■
という構造になります。
Cの配列だと、イメージ映像は、
double a[5][2];
□■□■□■□■□■
ですよね。
このメモリ配置のみが若干ネックです。ということで、
NArray.float(x, y, z)
と宣言した配列は、
a[z][y][x];
として受けてやる必要があります。
以上のことを踏まえると、ラッパ関数は下記のようになります。
// メモリ確保 double** allocate_2d_array(int nx, int ny) { double** a; int y; a = (double**)malloc(sizeof(double*) * ny); for(y = 0; y < ny; y++) { a[y] = (double*)malloc(sizeof(double) * nx); } return a; } // メモリフリー void free_2d_array(double** a, int nx, int ny) { int y; for(y = 0; y < ny; y++) { //free(a[y]); // NArrayからアクセスするので、freeしてはいけない。 } free(a); } // Cの多次元配列とNArrayの1次元配列のリンク void link_2d_data(double* src, double** targ, int nx, int ny) { int y; for(y = 0; y < ny; y++) { targ[y] = &(src[y*nx]); } } // 多次元配列での配列操作 void array_2d_init(double** array, int nx, int ny) { int x, y; for (y = 0; y < ny; y++) { for (x = 0; x < nx; x++) { array[y][x] = y*10+x; } } } // ここからピュアC void rb2c_array_2d_init(double* array, int nx, int ny) { double** a; a = allocate_2d_array(nx, ny); // メモリ領域の確保 link_2d_data(array, a, nx, ny); // Cの多次元配列とNArrayデータのリンク array_2d_init(a, nx, ny); // Cの多次元配列の操作 free_2d_array(a, nx, ny); // メモリ領域の解放 } // ラッパ関数 void wrap_array_2d_init(VALUE self, VALUE na, VALUE nx, VALUE ny) { //VALUE na2; struct NARRAY *n_na; //na2 = na_cast_object(na, NA_DFLOAT); GetNArray(na_cast_object(na, NA_DFLOAT), n_na); rb2c_array_2d_init((double*)n_na->ptr, NUM2INT(nx), NUM2INT(ny)); } // Rubyへのメソッド登録 void Init_testnarray() { VALUE module; rb_require("narray"); module = rb_define_module("TestNArray"); rb_define_module_function(module, "array_2d_init", wrap_array_2d_init, 3); }
a[z][y][x]としないといけないのが、ダサいのですが、とりあえず、Cのコードを使いまわせるようにはなります。