💎

mruby 初めの一歩

2024/09/01に公開

はじめに

以前から触ってみたいと思っていた mruby をようやく触ったのでメモ。

インストール

Github Readmeの通り。rake でビルドする方法が書いてある。古めの情報だとminirake でやってるが、3.3.0 だとminirakeの中でrake を呼んでるだけだった。

rake all test

mruby-3.3.0/bin にバイナリがいくつかできている(実は mruby-3.3.0/build/host/bin/mruby へのシンボリックリンク)のでパスを通しておく。

  • mruby - 本体
  • mirb - Rubyでいうirb
  • mrbc - バイトコードをつくるやつ。VM上で動かせる
  • mrbtest - テストフレームワークっぽい
  • mrdb - デバッガっぽい
  • mruby-config - mrubyプロジェクトのビルドやコンパイル設定を管理するためのツール
  • mruby-strip - mrubyバイナリからデバッグシンボルや不要な情報を取り除いて、サイズを削減するためのツール

チュートリアル

チュートリアル的なドキュメントがあまりなさげで見つからない。多分、Executing Ruby code with mruby が良さげなのでやってみる。

fibt.rb - Rubyで書いたフィボナッチ数列計算

def fibonacci(n)
  return n if n <= 1
  fibonacci(n - 1) + fibonacci(n - 2)
end

start_time = Time.now
fibonacci(35)
end_time = Time.now

puts "mruby Execution Time: #{end_time - start_time} seconds"

REPL

普通にirbっぽく

❱❱❱ mirb
mirb - Embeddable Interactive Ruby Shell

> def fibonacci(n)
*   return n if n <= 1
*   fibonacci(n - 1) + fibonacci(n - 2)
* end
 => :fibonacci
> 
 => nil
> start_time = Time.now
 => 2024-09-01 10:48:28 +0900
> puts fibonacci(35)
end_time = Time.now

puts "mruby Execution Time: #{end_time - start_time} seconds"
9227465
 => nil
> end_time = Time.now
 => 2024-09-01 10:48:29 +0900
> 
 => nil
> puts "mruby Execution Time: #{end_time - start_time} seconds"
mruby Execution Time: 1.261344 seconds
 => nil

ソースコードから実行

普通にrubyっぽく

❱❱❱ mruby fibt.rb
mruby Execution Time: 1.339467 seconds

CのコードにRubyを埋め込む感じで

やや無理やり感あるけど

#include <mruby.h>
#include <mruby/compile.h>

int main(void) {
  mrb_state *mrb = mrb_open();
  if (!mrb) { /* handle error */ }

  const char *ruby_code = 
    "def fibonacci(n)\n"
    "  return n if n <= 1\n"
    "  fibonacci(n - 1) + fibonacci(n - 2)\n"
    "end\n\n"
    "start_time = Time.now\n"
    "fibonacci(35)\n"
    "end_time = Time.now\n\n"
    "puts \"mruby Execution Time: #{end_time - start_time} seconds\"\n";

  mrb_load_string(mrb, ruby_code);

  mrb_close(mrb);
  return 0;
}

ヘッダの場所と、libmruby.a を渡してコンパイル

gcc -std=c99 -I ../mruby-3.3.0/include fibt.c -o fibt ../mruby-3.3.0/build/host/lib/libmruby.a -lm

実行

❱❱❱ ./fibt
mruby Execution Time: 1.243873 seconds

バイトコードにして実行

mrbc を使ってバイトコードにする

❱❱❱ mrbc fibt.rb 

みてみる

❱❱❱ hexdump -C fibt.mrb 
00000000  52 49 54 45 30 33 30 30  00 00 01 34 4d 41 54 5a  |RITE0300...4MATZ|
00000010  30 30 30 30 49 52 45 50  00 00 00 eb 30 33 30 30  |0000IREP....0300|
00000020  00 00 00 96 00 03 00 08  00 01 00 00 00 00 00 3c  |...............<|
00000030  63 03 58 04 00 5f 03 00  1d 03 01 2f 03 02 00 01  |c.X.._...../....|
00000040  01 03 03 04 23 2d 03 00  01 1d 03 01 2f 03 02 00  |....#-....../...|
00000050  01 02 03 51 04 00 01 05  02 01 06 01 3e 05 52 04  |...Q........>.R.|
00000060  51 05 01 52 04 2d 03 03  01 38 03 69 00 02 00 00  |Q..R.-...8.i....|
00000070  16 6d 72 75 62 79 20 45  78 65 63 75 74 69 6f 6e  |.mruby Execution|
00000080  20 54 69 6d 65 3a 20 00  00 00 08 20 73 65 63 6f  | Time: .... seco|
00000090  6e 64 73 00 00 04 00 09  66 69 62 6f 6e 61 63 63  |nds.....fibonacc|
000000a0  69 00 00 04 54 69 6d 65  00 00 03 6e 6f 77 00 00  |i...Time...now..|
000000b0  04 70 75 74 73 00 00 00  00 49 00 03 00 08 00 00  |.puts....I......|
000000c0  00 00 00 00 00 29 34 04  00 00 01 03 01 07 04 44  |.....)4........D|
000000d0  03 27 03 00 02 38 01 01  04 01 3f 04 01 2d 03 00  |.'...8....?..-..|
000000e0  01 01 05 01 3f 05 02 2d  04 00 01 3c 03 38 03 00  |....?..-...<.8..|
000000f0  00 00 01 00 09 66 69 62  6f 6e 61 63 63 69 00 4c  |.....fibonacci.L|
00000100  56 41 52 00 00 00 2d 00  00 00 03 00 0a 73 74 61  |VAR...-......sta|
00000110  72 74 5f 74 69 6d 65 00  08 65 6e 64 5f 74 69 6d  |rt_time..end_tim|
00000120  65 00 01 6e 00 00 00 01  00 02 ff ff 45 4e 44 00  |e..n........END.|
00000130  00 00 00 08                                       |....|
00000134

実行

 ❱❱❱ mruby -b fibt.mrb
mruby Execution Time: 1.337975 seconds

-b がバイトコードだよと教えているはずだが、なくても動いた(自動判定?)

Cのコードにしてしまう

❱❱❱ mrbc -B fibt_symbol fibt.rb

これで、fibt.c ができて、その中のfibt_symbol にバイトコードがはいっている。

#include <stdint.h>
#ifdef __cplusplus
extern
#endif
const uint8_t fibt_symbol[] = {
0x52,0x49,0x54,0x45,0x30,0x33,0x30,0x30,0x00,0x00,0x01,0x34,0x4d,0x41,0x54,0x5a,
0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0xeb,0x30,0x33,0x30,0x30,
0x00,0x00,0x00,0x96,0x00,0x03,0x00,0x08,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x3c,
0x63,0x03,0x58,0x04,0x00,0x5f,0x03,0x00,0x1d,0x03,0x01,0x2f,0x03,0x02,0x00,0x01,
0x01,0x03,0x03,0x04,0x23,0x2d,0x03,0x00,0x01,0x1d,0x03,0x01,0x2f,0x03,0x02,0x00,
0x01,0x02,0x03,0x51,0x04,0x00,0x01,0x05,0x02,0x01,0x06,0x01,0x3e,0x05,0x52,0x04,
0x51,0x05,0x01,0x52,0x04,0x2d,0x03,0x03,0x01,0x38,0x03,0x69,0x00,0x02,0x00,0x00,
0x16,0x6d,0x72,0x75,0x62,0x79,0x20,0x45,0x78,0x65,0x63,0x75,0x74,0x69,0x6f,0x6e,
0x20,0x54,0x69,0x6d,0x65,0x3a,0x20,0x00,0x00,0x00,0x08,0x20,0x73,0x65,0x63,0x6f,
0x6e,0x64,0x73,0x00,0x00,0x04,0x00,0x09,0x66,0x69,0x62,0x6f,0x6e,0x61,0x63,0x63,
0x69,0x00,0x00,0x04,0x54,0x69,0x6d,0x65,0x00,0x00,0x03,0x6e,0x6f,0x77,0x00,0x00,
0x04,0x70,0x75,0x74,0x73,0x00,0x00,0x00,0x00,0x49,0x00,0x03,0x00,0x08,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x29,0x34,0x04,0x00,0x00,0x01,0x03,0x01,0x07,0x04,0x44,
0x03,0x27,0x03,0x00,0x02,0x38,0x01,0x01,0x04,0x01,0x3f,0x04,0x01,0x2d,0x03,0x00,
0x01,0x01,0x05,0x01,0x3f,0x05,0x02,0x2d,0x04,0x00,0x01,0x3c,0x03,0x38,0x03,0x00,
0x00,0x00,0x01,0x00,0x09,0x66,0x69,0x62,0x6f,0x6e,0x61,0x63,0x63,0x69,0x00,0x4c,
0x56,0x41,0x52,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x03,0x00,0x0a,0x73,0x74,0x61,
0x72,0x74,0x5f,0x74,0x69,0x6d,0x65,0x00,0x08,0x65,0x6e,0x64,0x5f,0x74,0x69,0x6d,
0x65,0x00,0x01,0x6e,0x00,0x00,0x00,0x01,0x00,0x02,0xff,0xff,0x45,0x4e,0x44,0x00,
0x00,0x00,0x00,0x08,
};

これを呼び出すコード fibt_main.c を以下のように書く

#include <mruby.h>
#include <mruby/irep.h>
#include "fibt.c"

int
main(void)
{
  mrb_state *mrb = mrb_open();
  if (!mrb) { /* handle error */ }
  mrb_load_irep(mrb, fibt_symbol);
  mrb_close(mrb);
  return 0;
}

コンパイル

gcc -std=c99 -I ../mruby-3.3.0/include fibt_main.c -o fibt ../mruby-3.3.0/build/host/lib/libmruby.a -lm

実行

❱❱❱ ./fibt         
mruby Execution Time: 1.270651 seconds

使い分け

Executing Ruby code with mruby から引用

REPL(mirb)は、主に初期開発段階で使用されます。mrubyの現在最も一般的な使用法は、ソースコード(.rb)であり、これはRubyをスクリプト言語として強調し、機械上でソースコードを変更することで簡単に修正できるという特徴があります。ソースコード(.c)は、自分のアプリケーションにmrubyを組み込む最も簡単な方法です。バイトコード(.mrb)は、Javaアプリケーションのような感覚を提供し、ファイルベースでインストールできますが、ソースコードへのアクセスは提供されません。バイトコード(.c)は、多くの人にとってmrubyを使用する最も複雑な方法かもしれませんが、同時にプログラム内でmrubyコードを最も効率的に実行する方法でもあります。

なるほど、スクリプトとして開発しつつ、リリースの時はCでコンパイルすればいいんですね。

mgem

mgem自体はRubyのgemみたい

❱❱❱ sudo gem install mgem
Password:
Fetching mgem-0.3.0.gem
Successfully installed mgem-0.3.0
Parsing documentation for mgem-0.3.0
Installing ri documentation for mgem-0.3.0
Done installing documentation for mgem after 0 seconds
1 gem installed
mgem list

300+ の mgem がある。

gem install mgem
mgem search <search-pattern>

参考

動かす系

仕組み系

Discussion