th135_ai ver1.02bリリース

http://wordpress.click3.org/garakuta/th135_ai.zip

・AIツールを起動したあと一回目に限り1P側が霊夢に変わってしまう不具合を修正。

共有メモリーにてやり取りしているわけですが、AIツールは二個起動させることも想定しているとか
AIか心綺楼かどちらかが生き残っている限り中身は残り続けるなどがあるため、共有メモリーを作成したほうが初期化しなければなりません。
そこのAI側の初期化にバグがあり、使用キャラ切り替え部分が未初期化だったわけです。

というわけでバグ修正のみです。
なので、ツールスレなどには出さずにここでひっそりと公開するだけにとどめておきます。

th135_ai ver1.02aリリース

http://wordpress.click3.org/garakuta/th135_ai.zip

何かバグ修正したとか機能追加したとかではなく、内部で使っているmrubyのバージョンを上げただけ。
その関係でもしかしたら何かバグがなおっていたり機能が追加されていたり早くなったりしているかもしれない。
というわけで、ツールスレなどには出さずにここでひっそりと公開するだけにとどめておきます。

th123_ai ver0.96リリース

http://wordpress.click3.org/garakuta/th123_ai.zip

ツールスレにて、実に一年以上前ですが要望の書き込みがあったので、今更ながら対応したのが今回のリリースになります。
また開発環境の作り直しの都合で、VisualStudioがまたバージョン変わったり、その影響でXP向けにもビルドできるようになったので余分なラッパー外したり、なんてことをしてました。
ちなみに、各種フラグは内部的には普通に持っていて、単に表に出していなかっただけという手抜きぶり。
なんで要望当時の自分は対応しなかったんでしょうね。

th135_ai ver1.02リリース

http://wordpress.click3.org/garakuta/th135_ai.zip

ツールスレにて公開された一輪AIが一戦目に限って装備が正しく行われないバグがあり
AIツール側の問題ではないかと調べたところ、キャラ別に装備分岐するようになっているがmainの外ではキャラクターIDが取れない、という問題によるものでした。
ので、そういった問題を解決しようとしていろいろあったのが今回のバージョンアップになります。

具体的には、使用キャラをAIスクリプト側から決定できる機能の追加、set_equipも動作に変更はありませんがドキュメント上でmainの外側だけにしろと書いていたのを削除しています。
その他はバグ修正や、便利クラスの追加などになります。

mrubyビルド設定項目解説

この投稿はmruby Advent Calendar 2013の24日目の記事です。
23日目はyamanekko さんのmrubyをRaspberry Pi(bare metal)で動かす: Windows編、25日目はyukihiro_matzさんの24時間一人mrubyハッカソンです。

はじめに

mrubyはカスタム性の高さも売りの一つで、/include/mrbconf.hを弄ることである程度動作を変えられるようになっています。
しかし、設定の多さに圧倒され、すべてデフォルトのまま使っているという人も多いのではないでしょうか?
そこで今回はわかる範囲で各種設定について説明してみようかと思います。

mrubyの設定(特にメモリーレイアウトに関する部分)は環境により動作が異なることが多々あります。
今回は筆者の開発環境であるVisualStudio2013にて試したものであり、gccやclangや特定環境専用のコンパイラなどでは結果が異なる可能性があることを念頭に置いてお読みください。

2013/11/25当時(git hash: f5bd87b9e6d0d8a84cf866b4847c1416e4f5c622 )の物です。
それ以降のmrubyを使用する場合は以下の解説の通りではない可能性があります。

2013/12/24 23:30追記
MRB_GC_TURN_OFF_GENERATIONALとMRB_GC_FIXED_ARENAの説明が間違っていたため、関連記述を変更しました。
また、間違いの指摘をしてくださったMatzさん、どうもありがとうございます。

MRB_USE_FLOAT

実数を扱う型をdoubleからfloatに変更します。
VS2013でデフォルト設定であればmrb_valueのサイズは16byteですが、これを定義すると8byteになります。
しかし当然ですがfloatになるので精度がかなり落ちます。
実数なんか使わないからメモリー消費量抑えたい場合などに使うといいでしょう。

MRB_INT16

整数を扱う型をint32_tからint16_tに変更します。
しかし、ポインターなどを扱う都合からmrb_valueのサイズが縮まったりはしません。
おそらくは16bit CPUなど2byte演算が常用される環境用なのだと思われます。

MRB_INT64

整数を扱う型をint32_tからint64_tに変更します。
MRB_INT16と同時には使えません。
デフォルトの設定(MRB_USE_FLOATやMRB_XX_BOXINGが無効の環境)では特にmrb_valueのサイズが増えたりはしないので、32bit範囲を超える演算を多用するなら有効にするのもよいでしょう。

MRB_NAN_BOXING

実数の特殊状態であるNaNの定義の一部を利用して値を投入することでメモリー消費量を落とす機能を有効にします。
MRB_USE_FLOAT、MRB_INT64とは同時には使えません。
一般的な環境であればこれが有効になるとmrb_valueのサイズが16byteから8byteになります。
ただし実数演算の実装依存なため、環境により動かない可能性があります。

MRB_ENDIAN_BIG

一部構造体のメンバーの配置順を変更します。
名前から誤解されがちですが、整数を格納するエンディアンがビッグエンディアンになったりはしません。
メンバーのアドレスが変わるぐらいしか変化がないので、通常の環境であればON/OFFどちらでも特に影響はない設定です。

MRB_WORD_BOXING

ポインターは下位2bitは使われないことと、整数の1bitを犠牲にすることでメモリー消費量を落とす機能を有効にします。
MRB_NAN_BOXINGと同時には使えません。
mrb_valueは4byteになりますが実数がポインターで保持されるため、実数を多用すると逆にメモリー消費量が増えてしまいます。
VS2013ではバグなのか私が知らない仕様なのか、MRB_INT16かMRB_INT64と共に定義すると一部ビットフィールドが無視されサイズが増えてしまいます。

MRB_FUNCALL_ARGC_MAX

mrb_funcall関数で渡せる引数の最大数を設定できます。
mrb_funcall関数はここで設定した分の長さを持つmrb_valueの配列をスタックに確保するので、スタックに余裕がない環境の場合は小さく設定するとよいでしょう。
(といっても配列一つなので大して変わりませんが)
ちなみに、ここで設定された以上の引数を要求するメソッドもmrb_funcall_argvを使えば呼ぶことができます。
なので、mrbgemなどどういう設定で使用されるかわからないものを実装する場合、mrb_funcall_argvを使ったほうがいいかもしれません。

MRB_HEAP_PAGE_SIZE

インスタンスなどに使用するヒープ領域の1確保単位を設定できます。
1確保単位と書きましたが、単位はbyteではなくインスタンスで、1024なら新たなヒープを確保せずに1024インスタンスを割り当てることができます。
またヒープを使用するのはあくまでインスタンスそのものだけで、arrayの本体である動的確保部分などは別途mrb_mallocで確保されます。

MRB_USE_IV_SEGLIST

インスタンス変数を保持するデータ構造をハッシュからリンクリスト形式に変更します。
インスタンス変数が少なければハッシュ計算が不要な分高速になるかもしれません。
ちなみにインスタンス変数一つにつき1要素ではなく、(執筆時点では)4つのインスタンス変数を保持できる配列が1要素のリストになります。

MRB_IVHASH_INIT_SIZE

インスタンス変数を保持するハッシュテーブルの初期サイズを設定します。
MRB_USE_IV_SEGLISTが定義されている場合は無視されます。

MRB_IREP_ARRAY_INIT_SIZE

何に使われているか不明です。
というより、grepしても出てこなかったので今はもう使われていないのではないかと思われます。
2013/12/24 23:30追記
hash: 9fab01caでmruby本体から削除されたようです。

MRB_GC_TURN_OFF_GENERATIONAL

世代別GCを無効にします(有効ではなく無効な点に注意)。
定義すると単なる3色インクリメンタルマーク&スイープになります。
逆にコメントアウトすると、追加で古いオブジェクトは暗黙でマークしてGCする処理が加わるようです。
GCの性能比較はよくわからないので割愛。

KHASH_DEFAULT_SIZE

Hashのデフォルトの初期サイズを設定します。
Hashクラスだけではなくmruby内部で使われるハッシュテーブル(名前空間の解決用など)にも適用されるようです。

POOL_ALIGNMENT

一括解放や確保できるメモリー空間として使用される独自のメモリープールにおいて、メモリー確保時に先頭アドレスをアラインするサイズを設定します。
メモリープールは主にパース結果など内部表現を保持するために使用されています。
アラインされないと速度が低下したり、そもそも読み書きに失敗するような環境において調整することを目的としているようです。

POOL_PAGE_SIZE

メモリープールの1確保単位を設定します。
メモリープールの容量が不足するたび、このサイズ分新たに確保して動作します。
当然ですが、大きくすれば処理速度が上がり、小さくすればメモリー効率が上がります。

MRB_STR_BUF_MIN_SIZE

文字列構築用バッファ確保(具体的にはmrb_str_buf_new関数)でメモリーを確保する際の最小サイズを設定します。
mrubyで扱う文字列全般というわけではなく、あくまでCレイヤーでなんらかの文字列を構築する際のバッファにだけ影響する様子。
メモリー再確保のコストを減らすためか、設定値以下のサイズを取得しようとすると自動的に設定値まで広げて確保されるようです。
mruby本体に限ればすべての使用箇所で適切にバッファサイズを指定しているので、いっそ小さな値にするとメモリー効率は上がるかもしれません。
(大して使用されていないので微々たるものでしょうが)

MRB_GC_ARENA_SIZE

Cレイヤーで処理する際の一時オブジェクト保存域であるarenaの初期サイズを指定します。
あくまで初期サイズであり、溢れるたびに1.5倍されていきます。
よほど特殊な例を除き、ちゃんとした実装であれば100を溢れることは滅多にありません。
自身のコード由来で大きくすることを検討している場合、下記のURLを参照のうえよく考えてから行うようにしてください。
arenaの詳細は http://www.rubyist.net/~matz/20130731.html を参照のこと。

MRB_GC_FIXED_ARENA

arenaのサイズを固定にします。
その場合のサイズはMRB_GC_ARENA_SIZEになります。
固定のメリットは若干の処理速度の向上と、arenaを大量に使うコードでクラッシュするため早期発見ができることです。
省メモリーが必須な環境なら、事前に固定に変更したうえで負荷テストなどを行うとarena溢れを防止できて効率的かもしれません。

DISABLE_STDIO

エラー時の出力などコンソールへの入出力を行わないようにします。
標準入出力先が存在しない環境など用と思われます。

ENABLE_DEBUG

デバッグ機能を有効にします。
有効だとmrb_stateにcode_fetch_hookというメンバーが増え、そこに関数ポインターを代入することで、1命令実行するごとにコールバックされるようになります。
当然ですがこれが有効のままだと処理速度は低下するので、有効のままリリースするのは推奨されません。

終わりに

いかがだったでしょうか。
書いてる側からすると、ヒープやプールなど独自に管理するメモリー領域が使われていたり、ON/OFFできる機能が意外と多かったりなど、知らなかったことが意外と多くて勉強になりました。
これを元にいろいろもっと気軽にmrbconfをカスタマイズするユーザーが増えてくれたら幸いです。

ではまた。

mrubyの小ネタ集

前回の関連で得た知見をそのまま忘れるに任せるのもちょっと惜しいと思ったので、小さく小ネタ集として残しておきます。

ローカル変数は最大約512個

mrubyのバイトコード上レジスタは最大でも512個しかありません。
そしてローカル変数1つにつき1つレジスタが割り振られるため、ローカル変数は最大で512個しか使えません。
さらに、メソッド呼び出しなど各種処理でもレジスタを使うので、前後のコード次第で事実上はもう少し少ない数になります。
足りなくなるということはないでしょうが、そういう仕組みなのだということは覚えておくと役に立つこともあるかもしれません。

リテラルの文字表現は最大1023文字

整数実数文字列といったリテラルを文字で表記するとき、最大文字数は1023文字となります。
文字列の場合は””を含まない本体の文字数、8進数は先頭の0もしくは0_を除いた文字数、16進数も同じく先頭の0xを除いた文字数が1023までなら使用できます。

少し前まではMRB_PARSER_BUF_SIZEという設定をいじれば変更できたのですが、私がしたバグ報告の結果、コンフィグ上から削除され変更は推奨されなくなりました。
一応/include/mruby/compile.hにまだ定義されているので、興味のある人は最大65535の範囲でいじってみましょう。

リテラルは使用した回数だけ定義される

短い文字列は毎回書いてしまうこともありますが、mrubyは11/30現在までのところ同一のリテラルをまとめる機能はありません。
実際同じ文字リテラルを複数使うコードをmrbcしたものをバイナリエディタで覗くと同じ文字列が複数個ならんでいるのが確認できます。

メモリー効率を考えるなら複数回同じリテラルは使わず、一度変数に代入し使いまわすようにするといいようです。

post引数という定義がある

post引数という定義の引数があります。
具体的には

def func(normal, *rest, post)

のpostという引数が相当します。

これは可変長引数の後の通常引数で、解釈は先頭から通常引数、末尾からpost引数と確定させていき、残りを可変長引数と解釈するようです。

mrubyの小ネタとして書きましたが、普通にrubyにもあります。

通常引数・オプション引数・post引数の数は31個まで

通常の引数の数は31個までしか受け取れません。
32個以上受け取るコードを書いた場合、特にシンタックスエラーにはなりませんがバイトコードは壊れます。
同様にオプション引数、post引数も31個しか定義できません。

合計が31個ではなく各引数が31個なので、組み合わせれば90個を超える引数でも可変長引数を使わずに受け取れます。

ローカル変数の名前はコンパイル時に破棄されている

2014/05/17追記:公式でローカル変数情報が実装されました、今ではKernel#local_variablesも動作するようになっています。

rubyであればKernel#local_variablesでローカル変数名の一覧が取得できますが、mrubyの場合コンパイル時にローカル変数名はすべて破棄されています。
ではどう管理されているかというと、変数一つにつきレジスタが一つ割り振られ、レジスタの1番地などという形で扱われます。
ちなみに、当然のことですがグローバル変数やインスタンス変数などはちゃんと変数名はシンボルとして処理されています。

終わりに

いかがだったでしょうか。
主にバイトコード内の定義上の限界から攻めていったので、最大数がどーたらというのが多かった気がします。
一応全部実際にコードを書いて試したつもりですが、何かミスなどあればコメントくだされば直しておきます。

ではまた。

mrubyにバグ報告をした@その2

mrubyを弄る過程でいろいろバグを発見して報告したので一応書くだけ書いておきます。

MRB_NAN_BOXING次第でコンパイル結果が変わる

MRB_NAN_BOXINGという設定のON/OFF次第でコンパイルしたバイトコードが変わる=>別の環境にもっていっても動かない、というバグ。

そもそもMRB_NAN_BOXINGとはは何かというと、doubleやfloatのNaN表現に余裕があることを利用し、そこに値を入れることでmrb_valueのサイズを減らそうというもの。

なぜそれでバグが起こるかというと、mrb_vtypeというmrubyの型情報をMRB_NAN_BOXINGで入れようとしたのだが、NaNの定義上の都合から0だけは入らず、すべて+1したものを入れるようにしていました。
それをそのままバイトコード側にも反映したため、MRB_NAN_BOXING次第で型のインデックスが1ずれ、動作しなくなっていたわけです。

バグ報告をしたところ、irep_pool_typeというバイトコード内に入れる専用の型情報が用意され、そちらから実行時に変換する形で修正されました。

実際に修正された時のdiff
https://github.com/mruby/mruby/commit/71354b91cb48ff3a5c1e3c09492d18a3c30efcb9

MRB_PARSER_BUF_SIZEが65536以上でバイトコードが壊れる

MRB_PARSER_BUF_SIZEが65536以上で65536文字以上のリテラルを使おうとするとバイトコードが壊れて実行できなくなるバグ。
あくまでバイトコードのロードに失敗するだけなので、ソースコードから直に実行する場合は問題ない。

まずMRB_PARSER_BUF_SIZEが何かから説明すると、パーサーで扱う最大のリテラル長を定義したもので、子の文字数以上のリテラルはパース時にエラーとされる。

で、なぜ65536文字以上だとだめなのかは簡単で、バイトコード上ではリテラルの長さに2byteしか割り当てていないため。

コンパイル時にエラーするべきでは?と報告したところ、そもそも65536以上にすることがないと判断されたのか、設定項目から削除され、1024固定となりました。

実際に修正された時のdiff
https://github.com/mruby/mruby/commit/ed0d9f0066eb541ae4ceddf47bdd85112feccddf

コンパイル時にレジスタ使い切るとクラッシュ

mrubyのVM上のレジスタは最大で512個なのだが、これを超えて使うようなソースコードを読み込ませるとクラッシュするというもの。
具体的にはローカル変数が512個あるコードなどが当たる。
(mrubyのVMではローカル変数一つにレジスタが一つ割り当てられている)

これは単にエラー処理の実装が間違っていて、解放済みメモリーを触ってしまっているだけ。
このエラー処理に分岐するのがレジスタ使い切りぐらいしか存在しないため、エンバグ後発見されなかっただけと思われる。

実際に修正された時のdiff
https://github.com/mruby/mruby/commit/56a93a3357ab9f50a4105980e0b4d39e7d400e3b

th135_ai v1.01リリース

http://wordpress.click3.org/garakuta/th135_ai.zip

バグ報告のあった「お互いが同時に射撃するなどするとTh135Data.player[0].objectにplayer2がownerのデータが含まれることがある」に対応した版になります。
原因は簡単で、キーが同一のobjectが複数個発生していて、その都合でobjectを取り違えていたというもの。
現状のままだとどうしようもなかったので、object_arrayメソッドを追加して、objectメソッドを非推奨としました。
一応playerのobjectに他方のobjectが入り込まないようにはしましたが、場合によっては同playerのobjectですら重複しそうなので非推奨です。

どうでもいい話ですが、バグ修正だけだしver1.00aにしようかとも思ったところを、紛いなりにもメソッド追加なので数字あげました。
個人的ルールとして、機能追加は数値up、バグ修正は末尾アルファベット追加すでにある場合はアルファベットをインクリメントとしています。