Shinjuku.rb #57で、"mrubykaigi"というタイトルの発表をした。

少し時間が立ってしまいましたが、Shinjuku.rb #57で話させていただきました。

shinjukurb.connpass.com

発端

弊社Repro, Inc.で定期開催している新宿.rbという地域コミュニティで、 「mrubyについて話しませんか」と人生初の登壇オファーを頂いたので快諾し、お話しさせていただくことになりました。

当日

本業がテンヤワンヤしていたので、予め資料を作っておいてよかった……。

発表した資料はこちら

https://gist.github.com/ksss/ff1dc20bcab3f7ab654cc40ce0df5aae

とはいえこの資料の内容は半分くらいしか喋っていません。

みんなどう思っているんだろうという所を知りたかったので、ディスカッションぽくトークできればいいなと思って、発表中もどんどん意見を求めました。

議題は「どうすればmrubyが流行るか?」と言うものだったのですが、様々な意見が飛び交い、思ったよりディスカッションぽくなったんじゃないかなと思います。

日頃から気になっていたmrubistの方々とも直接お話できて、思っていたよりmrubyへの関心が高まってるんじゃないかと手応えを得ました。

他の発表も非常に濃くて、自分では手が届いていない未知の世界を見せていただき、大変刺激になりました。

地味すぎて誰も気がついていないCRuby 2.5の新機能

did_you_mean gemがKeyErrorにも効くようになったよ

KeyErrorは指定したkeyに対するvalueが見つからなかったときに起こる例外で、IndexErrorから派生したものです。

KeyErrorが起こり得るのはHash#fetch Hash#fetch_values ENV.fetch Kernel.sprintf String#%の5つです。

このメソッド達で、探したkeyが見つからなかったときに近しい候補をサジェストしてくれるようになっています。

$ ruby
h = {foo: 1, bar: 2}
h.fetch(:bax)
Traceback (most recent call last):
    1: from -:2:in `<main>'
-:2:in `fetch': key not found: :bax (KeyError)
Did you mean?  :bar

実はCRuby 2.4でもrequire "did_you_mean/experimental/key_error_name_correction"と呪文を唱えると、Hash#fetchに対してdid_you_meanが効いていたのですが、やり方に気づかない上にHash#fetchprependでモンキーパッチする危ない実装だったので誰も使っていませんでした。*1

これはイカンと一念発起し、 KeyErrorに2つメソッドを追加しました。

bugs.ruby-lang.org

これで、NameErrorと同じく、エラーオブジェクトに原因となるオブジェクトを紐付けることができるので、モンキーパッチ無しでdid_you_meanを効かせることができます。

github.com

この辺で思いついた機能がついに実を結んだわけですね。

RubyでJSONをclassにmappingするやつと、2つの記法 - スペクトラム

ふるってご利用下さい。*2


*1:作者は私です

*2:本当はdid_you_mean作者のyuki24さんがRubyアドベントカレンダーに登録していたのを見て、ここで紹介されるだろうと思ってたけど、アドベントカレンダーの登録が消えたっぽいのでこのタイミングに。

mruby本体にmruby-methodを移管したよ

「mrubyにmethodメソッドはないのかな?」とおググりの皆さんに朗報です。

ksss/mruby-methodというライブラリでmethodメソッドは使えてはいたのですが、この度*1mruby本体にmergeしてもらいました。

github.com

理由としては、

ksss/mruby-methodを作ってからも「mrubyにはmethodメソッドないんですか」的なコメントがみられたり、

https://twitter.com/yukihiro_matz/status/788580277471711232 https://twitter.com/yukihiro_matz/status/777849074816995332 https://twitter.com/yukihiro_matz/status/938353972347146240

CRubyにも標準装備で環境依存の機能がないので入れやすかったり、

最近はmgemを本体に入れるのが流行ってたり *2

メソッドの持ち方が変わったときにメンテが面倒だったりしたからです。 *3

よりストレス無くmrubyを使ってほしいからです。

ふるってご利用下さい。

*1:と言っても去年

*2:偏見です

*3:一応直してからmergeした

今年買ってよかったもの

2017年に買ってよかったもの

Nintendo Switch & スプラトゥーン2

買ってよかったのか悪かったのか……、正直微妙ではあるが、ひたすらゲームが楽しかった。 ソフトはスプラトゥーン2しか持ってないのに5ヶ月遊べている。 スプラに飽きたらいったん休憩した方がいいと思う中毒性。

Nintendo Switch Proコントローラー

通称プロコン。これはスプラトゥーンを初めて2ヶ月後ぐらいに買った。 正直高いけど、使いやくはなっていると思う。ウデマエには全く影響しないと思う。 強いてあげるなら、スティックの感度が高いので無音移動しやすいとか、ある程度重みがあって、実際に武器を持っているかのような没入感がいいところだと思う。 電池はかなり持つ。充電は1週間に一度ぐらいに不安になって刺してるけどもっと持つかもしれない。 スティックの感度が高すぎて急に戻すと逆側に反応してしまうので、メニュー選択とかでは誤操作しやすいのがデメリットか。

ソファー

3万くらいで3人座れるソファーを買った。 ちょっとでかすぎたかもしれないけど、ほぼここが自分の居場所になってる。 自宅で仕事するときもだいたいソファーに座って作業している。 ポテンシャルはもっとあると思っていて、配置次第ではもっと活用できそう。

iPad

iPad miniを持っていたけど盛大に割れたので、2017モデルの無印iPad 64GB Wi-Fiモデルに買い替えた。 これまでiPad miniで十分と思っていたのは幻想で、どう考えてもiPadの方が満足感が高い。 理由は画面の大きさ。 iPad miniを使ってみてそれほど持ち出さないことがわかったし、なんなら持ち出したときに割れたのでもう持ち出したくない。。。 主に漫画(ジャンプ+とかkoboとか)と動画(YouTubeとかopenrec)に使っているので、大きい方が見やすくてうれしい。

2018年に欲しいもの

サイドテーブル

ソファーに座って仕事するときにコーヒー置き場がほしい

Razerのヘッドホン

これを使うとどこに誰がいるのか音でわかるようになるとかならないとか……!


来年もよろしくお願いします。

このブログは妻との相互企画でした。

shlyme.hatenablog.com

スプラトゥーン2ガチ初心者が5ヶ月かけてランクS+になるまでの軌跡

スプラトゥーン2を発売日の次の日ぐらいから初めて、ほぼ毎日プレイし続けて今日ようやく目標だったランクS+に到達した。

前作はプレイしていない。ゲームは好きだけど最近はポケモンとかだった。 FPSはほとんど経験ない。 こういう感じのゲームで近いのは、 GunZとかFEZとかはやったことあるぐらいか。

本当にスプラトゥーンばっかりしていたのでGitHubの草もわかりやすい感じになっている。

f:id:ksss9:20171222174803p:plain

うまくなるにはinputとoutputを等しく取るべし

大抵の物事はこの法則である程度上手くなれると思う。 inputとoutputをstream処理するのだ!

input == YouTube

スプラトゥーンにおけるinputとはYouTubeである。 YouTubeで上手いプレイヤーの動画を見まくるのだ。 僕はこれまでYouTuberなるものを見たことはなかったけど、YouTubeのサービス品質はすばらしい。 無料で高画質の動画を過去の分もライブも見れる。ライブ放送中もちょっと巻き戻したりできる。 openrec.tvなるサービスも初めて知ってすごいなあと思ったけど、課金しないと過去の分は見れないようだ。 ニコニコは見てない。

おすすめのYouTuberは

の3つである。(敬称略)

裏切りマンキーコング西澤さんは吉本のお笑い芸人であり、YouTuberであり、プロゲーマーでもある。 スプラトゥーン1の動画も大量にあげているのでおそらくスプラトゥーン業界では有名なんだと思う。 オールラウンダーを自称しているけど、わかば等の短射程ブキを使ったときのスピードが早すぎて理解が追いつかないほど。 環境毎に西澤さんの使うブキを参考に、自分の立ち回りも練習していた。 一番のおすすめ。

しをたんさんは正直そこまでめっちゃうまいわけではない(それでも自分よりはうまいんだけど)。 ネタで3.9系動画を上げることが多いので、このギアを付けるとこうなるみたいな知識が身につく。 検証や解説も丁寧でわかりやすい。

のわさんはプレイスキルが高いが西澤さんのように僕の理解を超えた上手さというよりは、僕でも理解できる上手さという感じで非常に参考になる。 戦略上の解説なんかも多めなので参考になることが多い。 淡々と勝つために必要なことを解説しつつ実践していく。

YouTuberを見る上で重要なのは、声の聞き取りやすさ、観ていて不快感がないかが重要になる。 紹介したYouTuberは、どれも声が聞き取りやすく、なんかいつも楽しそうだ。

他にもいくつかYouTuberを見ていたが、文句ばかり言うような人や、スプラトゥーンをプレイするモチベーションが下がってるなと感じると見るのをやめてしまった。

output == play

inputしたことをひたすら実戦で試してみる。 傘の動画を見たら傘を使い、スシの動画を見たらスシを使う。

さんぽもするし、試し打ちでの練習も重要だ。 上手いプレイヤーの動画では大体試し打ちでエイムを温めてから試合に望んでいた。

負けたらなんで負けたのか考えて、勝ったらなんで勝ったのか考える。 自分のプレイ動画を録画する機能がswitch本体にあるけど、30秒じゃよくわからない。

stream

inputとoutputを少しずつ処理して自分のものにしていく。 そう。stream処理である。 どちらか片方が多すぎるとバッファーが詰まったり、上達が頭打ちになってくる。

ひたすらreadしてwrite、readしてwriteだ。

よく使うブキ

わかばシューター/もみじシューター

機動力が高く、ちょこまか動いてひたすら塗ってスペシャル撒く役。 ブラスターやローラーやってると自陣の塗状況がいかに大切かよく分かる。 ボムが基本的な攻撃手段。あとはメインで塗って塗って塗りまくってスペシャルを吐きまくる。 いつの時代でも安定している。

スプラシューター/スプラシューターコラボ

ガチマッチは味方の武器構成が重要なので、オールラウンダー的なブキがやはり強い。 4人ブラスターはキツイけど、4人スシでも全然強い。 機動力もあるしキルも取れるし塗りもできる。 ひたすらスシを使ってこれが基礎だぞ!と練習していた時期があった。 今も強いと思う。

プライムシューター/プライムシューターコラボ

アップデートで強くなってから使うようになった。 撃ち合いになったときに勝ちやすい。 チームの守備要員という感じ。 バブルは使いどころが難しかったけどガチアサリで活躍できるようになった。

スプラローラー

Aランクから必要になるスキル「センプク」が習得できる。 単純に爽快感があるので楽しいブキ。 メインとイカニンとカーリングボムのシナジー効果が高い。

N-ZAP85

キューバンボムがつかえるスシという感じ。 オールラウンダー的なブキなのでどの試合でもそれなりに仕事ができる。 特にホコで勝率が高くて、対物をつけたZAPでA+〜S+まで一気に行けた。開幕でホコを割れる確率が高まる。

これまでの軌跡

とはいえ最終的になんでS+に行けたのかよくわからない

ガチホコを対物ZAPでやってたら何故か勝率が高く勝利が重なっただけなのかもしれない。 たまたまステージ・ブキ・ギアが自分にマッチしたのかもしれない。 この辺を分析すると、ガチホコ以外も勝率が上がるのかなあ。

うまくなりたい……。

mruby v1.3

mruby v1.3 がリリースされましたね。

趣味mrubyウォッチャーとしてv1.2からv1.3で何が変わったのかを、個人的にまとめてみたいと思います。

注目すべきは、やはりmatzのcommit数。

もちろんmerge commitも含みますが、約半数のcommitがmatzのcommitになっています。

なぜmatzがここまでmrubyに力を入れるのか聞いてみたいところですね。

それではmruby v1.2 からv1.3への変更で何が変わったのか、ザックリ見ていこうと思います。

リリースノート

http://mruby.org/releases/2017/07/04/mruby-1.3.0-released.html

1年以上あった割には、表向きにはそこまで変化はない感じ? わりと最新のCRubyの文法やメソッドも入っていたりしますね。

Contributions

https://github.com/mruby/mruby/graphs/contributors?from=2015-11-17&to=2017-07-04

v1.2が出た日からv1.3が出た日にしているので若干正確ではないのですが、やはりmatzのcommit数はダントツですね。

Many bug fixes

やはり注目すべきはShopifyからの大量のbug報告と、これら全てを丁寧に対応されたmatzの修正の応酬でしょう。

https://github.com/mruby/mruby/issues?q=is%3Aissue+author%3Aclayton-shopify

SEGVの報告。free後のメモリや、確保した領域外のメモリなど、触れてはいけないメモリを触れてしまっている部分の修正。GCのバグ修正。意図しない無限ループの修正。諸々含めて約200のissueが上げられ、閉じられています。

shopifyとmrubyの関係はbovi氏のblogが詳しい。

The 500.000$ release - mruby.sh

こちらからの目線での印象としては、正直「そんな重箱の隅まで……。」というケースもあったのですが、 ユーザーにコードを書いてもらって実行するようなシステムだと、「ユーザーにシステムを止められるようなコード書かれると困る」というのは分かる話。

おかげでmruby本体が原因でSEGVが起きるようなケースは、重箱の隅を含めてもそう滅多なことでは起こらないようになったんじゃないでしょうか。

mrb_yieldでbreakできない問題

折角なのでどういうbug fixがあったのかひとつ抜き出してみましょう。

この問題が修正されたのは1.3が出る直前のことでした。

v1.2では、実は以下のようなコードはうまく動きません。 breakしているのに、ループが終わらないのです。っていうかSEGVします。

$ mruby-1.2.0
"aaa\nbbb\nccc\n".lines do |line|
  p line
  break
end
#=> "aaa\n"
#=> "bbb\n"
#=> "ccc\n"
[2]    72458 segmentation fault  mruby-1.2.0

これは、Cで実装されたメソッドでmrb_yield系が使われていた場合にbreakを利用すると発生していました。

breakreturnとほぼ同じ扱いにしているので、rubyで書かれたブロックならbreakで以降のコードは飛ばせるのですが、 C側のコードは引き続き実行してしまうのでループを止められないという問題があったようです。

Rubyコード上はbreakしているのに、C側のコードは止められないので不整合が起こり、SEGVしているものと思われます。

この問題はbreakを例外扱いすることでlongjumpし、Cのレベルでもメソッドのコードを飛ばせるように修正されています。

Allow `break` from a block called by `mrb_yield`; close #3359 · mruby/mruby@d4d99dd · GitHub

パフォーマンス

「パフォーマンスは早くなったの?」

ということで、mrubyに添付されているbenchmarkスクリプトで計測してみました。

buildの設定はfull-coreにしただけでその他は初期値です。(OSX 10.12.5 clang v8.1.0)

$ time mruby-1.2.0 benchmark/bm_ao_render.rb > /dev/null
mruby-1.2.0 benchmark/bm_ao_render.rb > /dev/null  20.22s user 1.44s system 98% cpu 21.942 total

$ time mruby-1.3.0 benchmark/bm_ao_render.rb > /dev/null
mruby-1.3.0 benchmark/bm_ao_render.rb > /dev/null  19.74s user 0.11s system 98% cpu 20.134 total

$ time mruby-1.2.0 benchmark/bm_fib.rb > /dev/null
mruby-1.2.0 benchmark/bm_fib.rb > /dev/null  12.32s user 0.03s system 99% cpu 12.387 total

$ time mruby-1.3.0 benchmark/bm_fib.rb > /dev/null
mruby-1.3.0 benchmark/bm_fib.rb > /dev/null  13.33s user 0.05s system 98% cpu 13.529 total

$ time mruby-1.2.0 benchmark/bm_app_lc_fizzbuzz.rb > /dev/null
mruby-1.2.0 benchmark/bm_app_lc_fizzbuzz.rb > /dev/null  37.46s user 0.45s system 98% cpu 38.559 total

$ time mruby-1.3.0 benchmark/bm_app_lc_fizzbuzz.rb > /dev/null
mruby-1.3.0 benchmark/bm_app_lc_fizzbuzz.rb > /dev/null  41.14s user 0.37s system 99% cpu 41.850 total

$ time mruby-1.2.0 benchmark/bm_so_lists.rb > /dev/null
mruby-1.2.0 benchmark/bm_so_lists.rb > /dev/null  5.90s user 0.17s system 97% cpu 6.197 total

$ time mruby-1.3.0 benchmark/bm_so_lists.rb > /dev/null
mruby-1.3.0 benchmark/bm_so_lists.rb > /dev/null  48.57s user 0.53s system 99% cpu 49.492 total

うーん、そこまで劇的な変化はなさそうですね。

やはり、mruby v1.3の目玉は大量のbug fixということでしょうか。

bm_so_lists.rbはCRubyだと1sとかなのでこれは……見つけちゃったかもしれないですね……。

一応報告しておきましょう。

Performance regression for benchmark/bm_so_lists.rb · Issue #3737 · mruby/mruby · GitHub

[追記]なおしました。

Should only check frozen fix #3737 by ksss · Pull Request #3739 · mruby/mruby · GitHub

v1.4……?

keyword arguments

https://github.com/mruby/mruby/pull/3629

実はkeyword argumentsに対応するPRは既に来ています。

が、v1.3には取り込まれませんでした。

リリースノートにもRemaining Bugsとしてこっそり含まれているので、ゆくゆく対応されるのではないでしょうか。

※ここは個人のブログです

個人のブログなので、ここからは自分がやったことをまとめます。

Kernel#caller書いたのオレオレ

Cレベルではほぼ同じ機能があったので、これをRubyの世界でも使えるようにしただけ。

引数の扱いが意外とめんどくさかったりしました。

これでデバッグが楽になる場面が増えたはず……!

Proc#initialize消したのオレオレ

これまでは、Proc#initializeでProcオブジェクトの初期化を行っていたのですが、Proc.remove_method(:initialize)と凶悪なことをすると、Procオブジェクトに必要な値が準備されず、いろいろな部分でSEGVが発生するようになります。

これではCレベルのメソッドでProcオブジェクトを扱う際に、常にProc.remove_method(:initialize)されたことも考慮しないといけなくなってしまいます。

ところでCRubyを見てみると、なんとProc#initializeは定義されていないではありませんか。 つまりこういう問題は、CRubyではProc.remove_method(:initialize)にはProc#initializeを定義しないことによって対策していると判断。

mrubyでもProc#initializeを削除して、Proc.newメソッドを独自に定義するようにしました。*1

Proc.newで初期化するようにすれば、たとえProc.newが消されたとしてもProcオブジェクトが作れないのだけなので問題ありません。

Contributionsの二番目にいるのオレオレ

https://github.com/mruby/mruby/graphs/contributors?from=2015-11-17&to=2017-07-04

mruby-specを使って、mrubyとCRubyの挙動の不整合をひたすら直すということをしてました。 またその際にみつかったバグなども直したり、難しすぎて直せないものはissueで報告などの活動をしていました。

序盤の活動はブログに書いていましたね。

結構Rubyの細かい挙動を知れたりして勉強にもなりました。

shopifyのやつもちょこっとだけ手伝った

Make string embad from shared by ksss · Pull Request #3657 · mruby/mruby · GitHub

例えばこちらのエントリーでは、shopify issueの https://github.com/mruby/mruby/issues/3651 を解決しています。

前知識としてStringオブジェクトは、shared, no-free, embed, normalなどの状態が存在し、今どの状態かによって適切に処理を分岐しないと、最悪SEGVするケースがあります(大抵は破壊的な操作)。

sharedなStringを変更してsharedの関係をやめるときに、これまではnormalなStringとしていたのですが、 issueにあるような一部のコードでは文字列の長さでembedかどうか判断していました。

じゃあこれを適切に場合分けして……とも思ったのですが、normalなのに長さはembed可能な文字列が生成できてしまうせいで、問題が起こっていると判断。sharedなStringを変更するときに、長さが短い場合はembedなStringにしてしまえば、問題も治るしメモリ消費を若干抑えられるしで一石二鳥です。

bugを一つ埋め込んでしまった

MRB_DISABLE_STDIO: String#upto native's mrb_str_upto requires snprintf · Issue #3714 · mruby/mruby · GitHub

で報告されているとおり、mruby-string-ext gemをMRB_DISABLE_STDIOでbuildできなくしてしまいました。。。

MRB_DISABLE_STDIOはその名の通り標準入出力を行う関数を一切禁止するだいぶリソースが厳しい環境向けのオプション。

そんな厳しい環境では標準添付ライブラリなんて使わないよね……。う……これで困る人、許してくれ。。。

mruby v1.3

みんな使ってね!

*1:ちなみに実装は、渡されたブロックのコピーを作っているだけ。

mruby-io-copy_streamつくった

github.com

特に問題意識を持っているわけではないけど、がんばって作ってみた。半月以上かかった。

mruby-io-copy_streamとは、mrubyでCRubyでいうIO.copy_streamが使えるようになるライブラリです。

IO.copy_streamは、ようはcatコマンドみたいなやつ。

ちょっとreadして、ちょっとwriteするので、巨大なIOでも一定のメモリで扱うことができる。

一度 mruby-ioにPRを送ってみたこともあるんだけど、以下の理由から止めた。

  • Rubyで書くなら普通にread/writeループでいいよね感があった。
  • このメソッドに求められる要件としては、やはり速度だと思う。(IO操作のほうが重いとは思うけど)
  • ということは、pread(2)やsendfile(2)を使いたい。
  • ということは、autoconf等を使いたい。
  • Cで書くと500行と大きめ。
  • ここまでくると、PRを送られてもmergeしにくいだろうと予想。
  • mrubyとしては、機能が細かく別れてプラガブルになっていたほうが、アプリケーションに無駄な機能を乗せずに済む(と思う)。

というわけで、ほぼほぼCRubyの移植として作った。 特にsendfile(2)を使えるのが面白いところだと思う。

また、特定のライブラリに依存しないように作った。

IOライブラリは最低限readwriteメソッドがあればいい。

filenoメソッドがあれば、fdを使ってシステムコールを呼ぶようにした。

これにより、mruby-ioはもちろん、mruby-tiny-ioでも、mruby-stringioでも、とにかく引数にオブジェクトを使えば、組み合わせることができるように工夫してみた。

よければ使ってみてください。