mrubyのメモリ使用量
ミニマムでどれくらいか調べるため、下記のようなプログラムでチェックしてみた。
使ったmrubyは今日取得したもの。mrbconf.h の内容は触っていない(デフォルトでオプションは全て外されているようなので)。
mrubyは中間コードをmrbcで生成できるので、VM側にパーサーを乗せなくてもmrbcで生成した.mrbファイルを渡せば実行することができる。
$ mrbc sample.rb
$ mruby sample.mrb
使ったソースコード
sample.rb
while 1 > 0 end
mruby.c
#include "mruby.h" #include "mruby/proc.h" #include "mruby/array.h" #include "mruby/string.h" #include "mruby/compile.h" #include "mruby/dump.h" #include <stdio.h> #include <string.h> int main(int argc, char **argv) { printf("- mrb_open start -\n"); mrb_state *mrb = mrb_open(); printf("- mrb_open end -\n"); int n = -1; if (mrb == NULL) { fprintf(stderr, "Invalid mrb_state, exiting mruby"); return EXIT_FAILURE; } FILE *fp = fopen(argv[1],"r"); printf("- mrb_load_irep start - %p\n",fp); n = mrb_load_irep(mrb, fp); printf("- mrb_load_irep end -\n"); if (n >= 0) { printf("- mrb_run start\n"); mrb_run(mrb, mrb_proc_new(mrb, mrb->irep[n]), mrb_top_self(mrb)); printf("- mrb_run end\n"); } return n > 0 ? 0 : 1; }
ヒープがどれだけ使われているか確認したかったので、allocf にちょっと仕掛けを入れた。
各所のヒープメモリ確保解放はこの関数に集約されている、はず。
アロケータの実装によっては大きく性能に影響しそうなところに見える。
src/state.c
/* ** state.c - RiteVM open/close functions ** ** See Copyright Notice in mruby.h *1340547113*/ #include "mruby.h" #include "mruby/irep.h" #include <string.h> #include <stdio.h> void mrb_init_heap(mrb_state*); void mrb_init_core(mrb_state*); void mrb_init_ext(mrb_state*); #define LOCAL_TOTAL_MAX 10000 struct memstack{ void* p; size_t size; }memstacktest[LOCAL_TOTAL_MAX]; int local_g_count=0; void local_add_mem(void* p,size_t size){ int i; for(i=0;i<LOCAL_TOTAL_MAX;i++){ if(memstacktest[i].p==NULL){ memstacktest[i].p=p; memstacktest[i].size=size; local_g_count++; return; } } printf("no stack mem!\n"); } void local_del_mem(void* p){ int i; for(i=0;i<LOCAL_TOTAL_MAX;i++){ if(memstacktest[i].p==p){ memstacktest[i].p=NULL; local_g_count--; return; } } printf("not hit!\n"); } int local_get_total_mem(){ int i=0; int total=0; for(i=0;i<LOCAL_TOTAL_MAX;i++){ if(memstacktest[i].p!=NULL){ total+=memstacktest[i].size; } } return total; } mrb_state* mrb_open_allocf(mrb_allocf f) { mrb_state *mrb = (f)(NULL, NULL, sizeof(mrb_state)); if (mrb == NULL) return NULL; memset(mrb, 0, sizeof(mrb_state)); mrb->allocf = f; mrb->current_white_part = MRB_GC_WHITE_A; mrb_init_heap(mrb); mrb_init_core(mrb); mrb_init_ext(mrb); return mrb; } static void* allocf(mrb_state *mrb, void *p, size_t size) { if (size == 0) { local_del_mem(p); printf("free[%d] %p\n",local_g_count,p); free(p); return NULL; } else { void* pointer = realloc(p, size); local_add_mem(pointer,size); printf("alloc[%d] %p, %d \n",local_g_count,pointer,(int)size); printf("alloc[%d] total %d \n",local_g_count,local_get_total_mem()); return pointer; } } 以下続く…
結果
Mac OSX 上で実行してみて、topと仕込みの出力の内容を確認してみる。
実行時の物理メモリ使用量(RSIZE):848K
mrb_openが終わった時点のヒープメモリ使用量:157816 byte
mrb_load_irepが終わった時点のヒープメモリ使用量:157737 byte
実行時のメモリが結構食ってるけど、これはどう解釈すればよいのだろう。
MacOSXでの実行ファイルの扱いは全然理解してないので、何か暗黙的なものが展開されてこのサイズなのか、mrubyが確保している分がこれだけあるのか、まだ自分には判別付かない。もう少し調べよう。
このmruby.cをビルドした場合のバイナリサイズを見てみる。
パーサー付きだと500KBくらあったが、そこを削った上記のmruby.cでは、
240,360 byte
だった。おお、小さい。
NXTの中に入ったというのも頷ける。ただメモリが全然足りてないので、どうやって64KBに収めたのか気になる。