Kishima's Hateda log

はてなダイアリー記事の保管庫

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に収めたのか気になる。