RubyKaigi 2017 に行ってきた(2): セッション内容、その他

技術記事パートとおまけです。先週会社で見てきたセッションの内容をしゃべったものが元になっています。

1日目

Keynote

Rubyの開発をどういうふうにやっているか。「ゆるふわRuby生活」というが全く内容はゆるふわでない。

Refinementsがstring interpolationの中でも機能するように変更したコミットをライブで入れたりした。

s = "foo"
using Module.new{
  refine(String){
    def intern
      "<#{self}>"
    end
  }
}
p :"#{s}"

本来 Symbol が返ってほしいのに String#internto_symみたいなの)が乗っ取られてるので Stringが返る!→直した

Fiber in the 10th year

Fiber導入10周年に際して、Fiberの使い方、Fiberにどのような変更が入ったかの話。

Fiber: 雑にいうとthreadのようなもの、ただしexplicit switchが必要。生成はFiberのほうが軽いがcontext switchが重い(今はマシになった)。

もともとはcoroutine(次にどこに飛ぶかを任意に指定できる)だったがsemi-coroutine(親子関係があるので終わったら親に戻る)に変更。

Fiber contextのswitchをどうやってやるか?

  1. コピーする。Ruby 1.8のThreadと同じ仕様。メモリ使用は少ないが、スタックサイズに比例する時間がかかる。
  2. POSIX makecontext/setcontextを使って定数時間でマシンスタック交換。
  3. もっとなんとかした。thread stateもpointer exchangeにした。5%早くなった/メモリサイズ30%減(ただしマシンスタック/VMスタックに比べてthread stateのサイズは誤差)

Auto Fiberというのが提案されている。

Fiberはスケジューリングがめんどいけどそれをインタプリタが自動化できれば最高では?→RubyのIOをFiberでやって、そのスケジューリングを自動でやる。既存コードを変えずにThreadより安全なものを導入できるが、非決定的なバグを生む危険性がある。今後入るかも?

dRuby on Browser

Opal(Ruby -> JS transpiler)を使ってdRuby(distributed objectのサポートをするGem)をブラウザ上で動かしてみた、という話。

Webで話せるプロトコルが必要→WebSocketのプラグインを開発した。

dRubyがブラウザで使えるようになると複数クライアントの間でオブジェクトを共有できる。

サーバーサイドのオブジェクトが透過的に触れることによるセキュリティ懸念はやっぱりあるみたいで鋭意対策中とのこと。

Do Androids Dream of Electronic Dance Music?

Deadmau5 の楽曲を素材にディープラーニングで楽曲自動生成をしたという話。

93.4% Python 4.8% Ruby 1.8% Shell WTF!? This is RubyKaigi!(スライドより引用)

I quit my job to write my own language: Goby

Rubyに影響されてGoで実装されたOO言語Gobyの話。

Goファイルをプラグインとしてコンパイルして利用するプラグインシステムを持つ(ただしLinux only)。

Unused Expressionがなぜ問題になるか(単純な実装だとスタックオーバーフローを引き起こしうる)、という話の解説。

  • 定数のsetはスタック上から値をpopしない
  • すべての式は必ず値を返す
  • すべてのstatementの終わりにpop instructionを置く

パフォーマンスは落ちるがStack Overflowで死なないVMになった。


2日目

The Many Faces of Module (Day2 Keynote)

型の話、concurrencyの話をしたので次はperformanceの話?→しません。3日目のKeynoteがそれ。

RubyのModuleとは何者か、という話。

オブジェクト指向の歴史。Simula(1968): Object, Class, Inheritance, Differential Programmingを備えていた。「プログラミング言語界のオーパーツ」。

継承: Simulaはsingle inheritance。しかし現実には親は2人いるし、roleを考えたらもっとある。

多重継承: 単純ではない。ダイヤモンド継承問題が発生する。

もっとシンプルにする方法はないだろうか?→mixin。LispではFlavorsともいう。

RubyのModuleの役割:

  1. mixinの役割として作られた
  2. Namespaceとして用いるケース
  3. singletonとして使うケース。module FileUtilsが相当。
  4. メソッドの集合としてのmodule。module_function
  5. "method combination"の単位としてのmodule

メソッド結合とは?→ Module#prepend の話。Railsとかで使われてたAlias method chainの改善のために導入された。

Method CombinationはCLOS(Common Lisp Object System)の用語。メソッド呼び出しのbefore/after/aroundをフックできる。

Monkey patchingの語源: Gorilla patching <- Guerrilla patching

(6) Refinements

RubyのRefinementsはlexical。特定のスコープにだけ機能を追加する。C#のextensionのようなもの。

(7) 提案されているだけだけど、structural typeとしてのmodule

module Str
  def to_s; end
  def to_str; end
end
# check if obj is string-like
obj.conform(Str)

Ruby, Opal, and WebAssembly

DXOpalという、DXRuby(Ruby向けのゲームライブラリ)をOpalで移植したものを作った人の話。

実装状況: 2Dゲームに必要な機能のサポートはできている。3Dはまだ。matter.jsと組み合わせて物理シミュレーションができるデモも。

%x{ ... } という表記でRubyコード中にJavaScriptを交ぜ書きできる。

WASMを使っているのは衝突判定の部分。衝突判定はけっこう重い。素のOpalだと26FPSくらいしか出ないが52FPSくらいになる。しかしWebAssemblyは「JSを速くするために用いるものではない」(公式のドキュメントより:JSでも同じくらい出る)。

WebAssemblyは「サイズを小さくしてロードを速くする」ためのもので、「実行を速くする」ものではない。

What visually impaired programmers are thinking about Ruby?

音声インターフェースのエディタの話。

点字ディスプレイでも補完、カーソル上の文字のblinkができる。

スクリーンリーダーがインデントを読み上げる際にはbeep音が鳴って、インデントの深さはbeep音の音程で示される。

モダンエディタでaccessibleなのは?: Visual Studio CodeはWeb Accessibilityという標準に準拠した実装がある。EclipseのIAccessible2 API、Dynamic Language Toolkit。

Rubyにもとめているもの: 読みやすい書きやすい、コード量が少ないこと。

rdocにはランドマークが入っているのでスクリーンリーダーでも読みやすい。YARDはこのへんまだまだ。

Automated Type Contracts Generation for Ruby

JetBrainsのRubyMineのTech Leadの人。

IDE的に)なぜ型は重要か:メソッドの定義へのジャンプ、バグ予測(NameError)、IDEの速度向上

x = {:a => '1', :b => '2', :c => '3'}
p x.length, x.downcase

Rubocopではスタイリング指摘はするが、 x.downcase があることによるエラーに気づくことはできていない

どのようにしてType contractを自動生成する?

  1. Ruby VMにattachしてすべてのcallについて型情報を得る。 -> TracePoint APIを使用
  2. 生のcall dataをtype contractに変換する。 -> メソッドの入力、出力の型のタプルから決定性有限オートマトンを構成
  3. 集めた情報を共有する。 -> TypeScriptみたいな感じ

(正直ここの内容けっこう難しくてよくわかってない。動画や資料がまだ出てきてないので出てきたら再確認したい)

Type Checking Ruby Programs with Annotations

計4つのRubyでの型の実装を作ったことのある人の話。今回の方針はLocal type inferenceとStructural subtypingによるもの。 Rubyにおける静的型の実装としてはDiamondback Rubyという実装がある。しかしこれはpolymorphic typeを推論できない。 polymorphic type inferenceでは一部のRubyのビルトインのもの(Arrayとか)に型を与えられない。

型システムに対する要求としては以下のようなものがある:

この制約を緩めたらどうなる?

RubyにおけるGradual Typingの実装: (GH) soutaro/steep

アノテーションなしのものは型チェックしない

局所的な型推論によってアノテーションが必要な範囲を限定

型を定義する別の言語(ヘッダファイルのような)を用意

Bending The Curve: Putting Rust in Ruby with Helix

RustでRubyの拡張が書けるよという話。

HelixはRuby gemでもありRust crateでもある。

実装詳細:Rustに対するDSLとしてはパーサーを一から実装するのではなくRustのマクロシステムを使った。ただこれだとrecursion limitに引っかかることが多いので、明示的にマクロのrecursion limitを上げてあげる必要がある。


3日目

Introducing the Jet Programming Language

(個人的に今回最注目だったのはこれ)

本人曰くRuby Flavored Erlangとのこと。

BEAMで動くRuby-like languageとしてはElixir, Reia, ErRubyがある。このうち、「オブジェクト指向で」「コンパイルされる言語である」という性質を満たすのはReiaだが開発が止まってしまっている。

Erlangで書けるものはJetで書けるようにしたい」を基準に言語仕様を設計。

Jetはクラス継承を持たない。

Rubyのブロックに相当するものは単純な無名関数 + trailing closureとして実現*1

ConcurrencyのためのHigh level interfaceを持つ。

meta Actor : Actorメタクラス。これを指定されるとnewの代わりにspawnが定義される。軽量プロセスに紐づくオブジェクトが生成される。async/awaitcast(一方的な呼び出し)も定義される。

  • lexer: leex
  • parsing: yecc
  • Erlang ASTへの変換: erl_syntax
  • bytecode生成: compileモジュール

インスタンスの状態は tag(インスタンスであることを示すもの)、State(immutable hash)、メソッドテーブル(method名とarityと関数への参照を示すimmutable map)の3つ組で表現。

Pattern Matching in Ruby

if %p([:ok, x]) =~ [:ok, 200]
  p "Second Element is #{x}"
else
  p "Not Match!"
end

みたいなsyntaxを導入してパターンマッチができるようにした、という話。その実質はElixir知ってたらまあほぼその通りなので省略するとして…

ほとんどRubyを使っての拡張、他はparse.ycompile.cに若干手を加えた程度。実際のdiffはこのリンク分が相当。*2 ASTの構築・比較と変数の束縛をするモジュールはlib/pm以下。

実装の詳細でいうと、パターンマッチをする前に PatternMatch.save_binding を使って変数の束縛を保存しておく必要があって、バイトコードへのコンパイル時にパターンの前に自動的にそれを追加するらしい。

Memory Fragmentation and Bloat in Ruby

Memory Bloat = 急激なメモリ上昇の後memory usageが下がらないやつ。何者か、どう対策するか。

だいたいの原因: User.all.each, User.whereの巨大なやつ

HTTPはステートレスでtransactionalなハズなのに何でfragmentationが起きるの?→GCされていないものが存在するから。

allocatorの問題。glibcのper-thread memory arenaはmain arenaに対するcontention(lock解放待ち)を減らすがfragmentationが起きる。こいつがRubyにおけるfragmentationの原因では?→カーネルパラメータで調整しよう

提案:allocationを減らす、managed mallocをする、jemallocを使う

Rubyレベルでどうにかできることは最初以外少ない…)

Improving TruffleRuby’s Startup Time with the SubstrateVM

Oracleの人によるTruffleRuby(Rubyの代替VM)シリーズの一つ。TruffleRubyはRuby core library specベースで96%、language specベースで99%準拠。

Truffleとは: 高速なランタイムを生成するためのツールキット。partial evaluationを使ったGraalというJITを持つ。複数言語対応はfirst-class feature。

startup timeが遅いことが問題だった。現時点ではピークパフォーマンスがウリで、optcarrot(ファミコンエミュレータを使ったベンチマーク)ではCRubyの8.5倍FPSが出る。Ruby3x9!

JRubyなどのalt-rubyも同様の特性をもつ。devとprodで別のRubyを使い分けるという方法もあるがリスキー。

なのでSubstrateVMで起動時間を改善した、という話。

SubstrateVM: JVMのうち到達可能で必要なクラス/型のみをコンパイルするAhead-of-Time compiler。

問題点としてはJavaのクラスのダイナミックロードができない、unsafeを使ったcallに注意が必要、"megamorphic call"*3には@TruffleBoundaryを指定する必要がある

Closing Keynote: Towards Ruby3x3 Performance

3x3は可能か? -> CPU-intensiveなものについては可能だが、メモリやI/Oに支配されている部分は無理。実質的には「Ruby3までに根本的なパフォーマンス改善がもう1段階必要だよね」という話。

(1) Stack-based instructionの代わりにRTL*4 instructionを使う

これによってデータの依存関係の発見がしやすくなり、インタプリタJITでコードの共有ができてパフォーマンス改善ができる。

→平均で27%のパフォーマンス改善が見られた。しかしこれでは十分でない。

JITを自作することについて。

  • WebKit: LLVM→自作JITでJetstream, Kraken, Octaneを高速化
  • V8: LLVMにポートしたらSunspiderが高速化された

結局実際どうなのよ?

→実装/メンテナンスにかけられている努力の総量からGCC/LLVMのほうがよいだろう。特にサーバーサイドの長期に渡って動くプログラムではよく効くハズ。

GCC/LLVMJITを実装するには?

  • LibGCCJIT, MCJIT, ORC
  • Cコードを生成: JITされたものよりデバッグしやすい。Cだし。junky approachと人は言うけど「ちゃんと」やればそんなことはない。

MJITとはC code generation + Pre-compiled HeadersによるJITMRI JIT、Method JITの略。

RTL instruction, MJITはCRuby(MRI)に入るか?→まだわからない。

それでもGCC/LLVMによって、新たな依存関係を導入せず、デバッグが容易で、ライセンスの地雷原に踏み込まないJITを導入することができる。


見れなかったやつで気になってたやつ

  • API Development in 2017
  • mruby gateway for huge amount of realtime data processing
  • Hanami - New Ruby Web Framework
  • Handling mails on a text editor
  • Tamashii - Create Rails IoT applications more easily

次のRubyKaigi

は仙台で、5/30-6/1の3日間だそうです。

miscellaneous remarks(ここから微妙に非技術混ざる)

  • ここには書いてないですが、右代入演算子を導入するかでだいぶ議論が沸騰してた。個人的にはあってもなくてもよいが、nobuさんがKeynoteの途中で |> と書き出して、Elixirユーザーとしてこの演算子で右代入を導入されたらRubyを嫌いになりそう。
  • コンピュータアーキテクチャとか言語処理系の講義で習った内容がじわじわ効いてきてる(今更)。Closing Keynoteとかで特に。とはいえリアルタイムでは追いかけられなかった内容がかなりあるのでビデオがあるのは助かる。家でMagicとかAge of Empires 2の動画流す代わりに見るようにしよう。
  • 非技術記事の部分で書かなかった話について。今回は #やんちゃハウス という @yancyaさん が集めてたAirbnbに相乗りしました。おかげさまで相当な旅費の圧縮に成功した上に多くのRubyistと絡むことができ非常に楽しいRubyKaigiを過ごせました。ありがとうございました!

  • 関連して、今度から5日以上の旅行の場合に宿泊施設のランドリーの有無を調べよう。7日分の着替え+若干の予備を持っていった関係で荷物が相当多かったが、やんちゃハウスに洗濯乾燥機がついてたため実際に使ったのはかなり限定的だった。昨年から比べると圧縮袋とか汚れ物入れとかを用意したのは進歩したが…
  • 2日目終了後のRubyKaraokeで歌ったもの、覚えてる限りのメモ。4階階段最寄りの「アニソン部屋」だった。まあ部屋成立初期に「ようこそジャパリパークへ」が入ったらそれはそう。機種はDAMなので安定初手であるところの「凛として咲く花の如く」が入れられなかった…。
    • コネクト(間奏部分の12話まどかセリフ込み)
    • メルト(久しぶりに一番上のA当てられた!)
    • ライオン
    • レッツゴー!陰陽師
    • High Free Spirits(初手で探したけどなぜか出てこず、作品名で入れたらちゃんと出てきた。「セキュリティクラスタだからはいふり知ってるのか」とは)
    • 海色
    • SAVIOR OF SONG(真ん中のラップを覚えてなかった)
    • 夏影
    • あと少なくとも2つはあったハズだが覚えてない
  • LTをした話は気が向いたら独立記事で書く。

*1:学部の言語処理系の授業でも普通はこうだと習ったので、RubyがProcオブジェクトとかになっているというのに対してこういう言及をしているものと思う

*2:最初のコミットが1ヶ月前…!?

*3:3つ以上の実装を通る関数呼び出しのことを指す用語

*4:Register Transfer Language