vagrant-global-statusをmrubyで書いた
前提
vagrant にはもともとglobal-statusオプションがあり、以下のように仮想マシンの状態をリストすることができます。
$ vagrant global-status
id name provider state directory
-------------------------------------------------------------------------------------
941f568 default virtualbox aborted /Users/juchino/my_vagrant/centos64_oracle
41ed4ec default virtualbox aborted /Users/juchino/my_vagrant/mruby_target
9e4777a default virtualbox aborted /Users/juchino/my_vagrant/mruby_host
00dcd43 default virtualbox poweroff /Users/juchino/my_vagrant/redmine2_5_2
fc42378 core-01 virtualbox poweroff /Users/juchino/my_vagrant/coreos/coreos-vagrant
121afc9 default virtualbox aborted /Users/juchino/my_vagrant/centos56_mruby
・・・
ただ、Rubyの処理系を経由するため、結果が返ってくるまで結構待ちます。 自分のMac Book Airで計測すると、
$ time vagrant global-status
・・・
vagrant global-status 1.33s user 0.26s system 83% cpu 1.908 total
で、この処理速度を改善するため、Goで書き直されたのが、以下です。
このコマンドの処理速度は、自分のMac Book Airで計測すると、
$ time vagrant-global-status
・・・
vagrant-global-status 0.00s user 0.01s system 71% cpu 0.012 total
でした。 ちなみに、作者さんの環境での測定結果等は以下に載っております。
やったこと
このvagrant-global-statusを、mrubyで書いてCの中に組み込んでみました。
mrubyのコードのほうで大半のことをしています。 $HOME/.vagrant.d/data/machine-index/index をパースして、 必要情報(マシンのid, マシン名, プロバイダ, 状態, ファイルシステム上のパス)のみArrayにどんどんpushしていきます。 mrubyからCに渡すのは、このArrayとなります。
class Vagrant
attr_accessor :machines
def initialize()
@path = ENV['HOME'] + "/.vagrant.d/data/machine-index/index"
@machines = Array.new()
end
def get_machines_status()
file = File.open(@path)
text = file.read
file.close()
JSON::parse(text)["machines"].each do |machine|
@machines.push(machine[0].slice(0...7) + " " + sprintf("%-10s", machine[1]["name"].slice(0...10)) + " " + " " + sprintf("%-10s", machine[1]["provider"].slice(0...10)) + " " + sprintf("%-10s", machine[1]["state"].slice(0...10)) + " " + machine[1]["local_data_path"])
end
end
end
Cのほうでは、mrubyから受けとったArrayを表示しています。
mrb_value res = mrb_funcall(mrb, mrb_top_self(mrb), "main", 0);
for(int i = 0; i < RARRAY_LEN(res); i++){
char *a = mrb_str_to_cstr(mrb, mrb_ary_ref(mrb, res, i));
printf("%s\n", a);
}
ビルドの方法は以下の通り。
まず、vagrant-global-status.rbをC言語配列形式のバイトコードにします。
mrbc -Bvagrant vagrant-global-status.rb
これで、以下のようなCのコード(vagrant-global-status.c)ができます。
/* dumped in little endian order.
¦use `mrbc -E` option for big endian CPU. */
#include <stdint.h>
const uint8_t
#if defined __GNUC__
__attribute__((aligned(4)))
#elif defined _MSC_VER
__declspec(align(4))
#endif
vagrant[] = {
0x45,0x54,0x49,0x52,0x30,0x30,0x30,0x33,0x94,0xaf,0x00,0x00,0x04,0x7b,0x4d,0x41,
・・・
};
そして、上記を読み込むコードの方では以下のように書きます。
#include <stdio.h>
#include <string.h>
#include "mruby.h"
#include "mruby/compile.h"
#include "mruby/string.h"
#include "mruby/array.h"
#include "mruby/dump.h"
#include "vagrant-global-status.c"
int main (int argc, char *argv[]) {
extern const uint8_t vagrant[];
mrb_state* mrb = mrb_open();
mrb_load_irep(mrb, vagrant); # ここでバイトコードを読み込んでいる
mrb_value res = mrb_funcall(mrb, mrb_top_self(mrb), "main", 0);
for(int i = 0; i < RARRAY_LEN(res); i++){
char *a = mrb_str_to_cstr(mrb, mrb_ary_ref(mrb, res, i));
printf("%s\n", a);
}
mrb_close(mrb);
return 0;
}
以上のようにしてできたコード(main.c)をビルドすれば、1つのバイナリでglobal-statusができます!
測定!!
作成したmruby版vagrant-global-statusの速度を見てみました。
vagrant-global-status 0.00s user 0.00s system 65% cpu 0.011 total
Go版と大差ありません!でした。
ちなみに、今回使ったmrubyは、以下のモジュールを組み込む必要があります。
conf.gem :git => 'https://github.com/iij/mruby-io'
conf.gem :git => 'https://github.com/iij/mruby-dir'
conf.gem :git => 'https://github.com/iij/mruby-env'
conf.gem :git => 'https://github.com/mattn/mruby-json'