#RubyKaigi 2018 2日目に参加しました。

RubyKaigi 2018 2日目に参加してきました。お昼に弁当食べたあと、仙台城跡を見に歩いて行ってきました(帰りはタクシーだったけど)。天気が良くて満足度が高かったです。

f:id:Watson:20180601123836j:plain

My way with Ruby

活動されている内容のはなし。

  • フリーソフトウェアを使って Ruby でできることをたくさん増やす
  • ライブラリのメンテナンス
    • 130くらいメンテナンスしているライブラリがある

自分にとって必要だったから。

Web の feed

RSS/ATOM

  • RSS/ATOM の validation
  • Ruby 2.6 以降はキーワード引数に対応

REXML

  • REXML → Ruby Electric XML
  • ライブラリ作者と Ruby を通じてやりとりすることになった
  • コード懇親会しますよ
    • Matz も参加しますよ
  • 2010年からメンテナーになった
  • Ruby だけで書かれている
    • install が簡単
  • 未来の話
    • NodeSet オブジェクトが取れるAPIがほしい
    • CSS セレクタがあると良さそう
    • 優先度は高くない。一緒にやりたい人いる?

プレゼンテーション

  • Rabbit
  • 2004年にリリースした
  • 2010年から Matz も使い始めた
  • RDのサポートが必須
    • Ruby Document
    • Text ベースのマークアップ
  • スライドの公開
  • プレゼンテーションツールに必要な機能 → GUI
    • Ruby/GTK3
    • 不足している機能があったら追加してからプレゼンを作っているそう
    • Ruby/GI
      • 自動的に binding を用意してくれるものっぽい
      • 手で書くよりはオーバーヘッドがあるため遅い
      • JITで高速化したいそう
    • Ruby/Poppler
    • Ruby/GStreamer
    • rcairo
      • GC 関係で問題があった → Ruby 2.4 で直された
    • 簡単なインストール
    • rake-compiler

Test

  • test-unit
    • 2008年からメンテナーになった
    • グループ化の機能を追加した
    • データ駆動テスト
    • 逆順のバックトレース
      • Ruby 2.5 の機能を、test-unit でも同じ挙動にした
  • RR

全文検索

  • Rroonga
    • Rabbit のサイトとるりまサーチで使用している
  • I18n
    • YARD
    • RDoc
    • jekyll
  • groonga-client
  • Ranguba (WIP)

データ処理

  • CSV parser
    • 2018年からメンテナーになった
    • CSVフォーマットの問題点
      • パースするのが難しい、パースが遅い
  • Red Arrow
    • Apache Arrowの公式Rubyバインディングになった
    • Ruby/GI を使ってる
  • Apache Arrow
    • スゴイ速いデータフォーマット
    • いろんな言語がサポート
    • 最近のデータ処理界隈ではすごい大事な1ピース
  • Apache Parquet
    • スゴイ速いデータフォーマット
  • Red Data Tools
    • Red Data Sets
    • jekyll + jupyter notebook
    • Red OpenCV
      • Ruby/GI を使ってる
    • RubyData Workshop を今日の夕方にやるよ〜

Faster Apps, No Memory Thrash: Get Your Memory Config Right

docs.google.com

Appfolio のひと

Tiny Objects

Symbol みたいなもの。Extra なメモリ領域を取らないもの。

Small Objects

String みたいな 40-byte slot なオブジェクト。メモリページごとに 408 Slots が割り当てられる。

Big Objects

Slot のサイズにあわないような大きな領域を確保するオブジェクト。

GC

Ruby は 世代別 Mark/Sweep GC を採用している。 とても良いが、best-in-the-world でない。

  • Major GC は全てのオブジェクトをチェック
  • Minor GC は新しいオブジェクトのみをチェック

手動でGC を動かす方法

GC.start # major
GC.start(full_mark: false) # minor

GC のフェーズ

  • オブジェクトが増える → ゴミを集める → slots を拡張する

GC の Mark & sweep は時間がかかる

  • 小さなオブジェクトをたくさん使うより、大きなオブジェクトを1つ使う方がしばしば良い
  • gsub! や concat を使うと、ゴミが減り Mark & sweep で必要だった CPU 使用量をセーブできます。

RUBY_GC_XXXX という環境変数で GC を制御できる。(https://qiita.com/yuroyoro/items/14ec7079f6574ad74409 に書かれているようなもの)

  • 環境変数をセットすれば起動時から GC を調整でき起動時間を短縮できます。

EnvMem: A Memory Tool

https://github.com/noahgibbs/env_mem

gem install env_mem

jemalloc

Ruby は jemalloc をサポートしてる。 jemallocを使うとend-to-end でトータル10%-12% くらいスピードアップした。

スピードあげるためのソリューション

  • ゴミを少なく
  • 環境変数
  • jemalloc
  • 最新のRuby

フラグメンテーション

Slot を使うと、 ページ内の 408 Slots が解放されるまで移動できない

GC::Profiler

GC で何が起こっているか詳細を知りたかったら → GC::Profiler.enable

GC::Profiler.enable

# DO something

GC::Profiler.report

Guild Prototype

http://www.atdot.net/~ko1/activities/2018_rubykaigi2018.pdf

Guild の進捗の話

Background of Guild

Motivation

  • Productivity
  • Performance by Parallel execution
    • Ruby では Thread が並列動作しない
    • 多くの人々は複数の CPU コアをフルに使いたいと望んでる

デモ

  • 40スレッド動く CPU + ubuntu 16.10
  • fib(23) x 100_000 回
    • Guild を使うと13分で終わった
    • シングルスレッドだと 3.5時間
    • fib(9)くらいから Guild のほうがスピードアップし始める
  • ruby/test/**/* のワードをカウントする
    • シングルだと1.73 sec
    • 40 Guilds だと 6.13 sec でシングルより遅くなった
    • 遅くなったのは GC のせいらしい

仕様

  • non-sharable
    • mutable object (Array, String...) を Guild 間で共有できない
  • sharable

    • immutable objects は Guild 間で共有できる
    • Class/Module objects
    • Special mutable objects
    • Isolated Proc
  • Guild間の通信 API

    • Actor model
    • shareable object を送信する
    • non-shareble object を移動する

Guild の実装

  • Before
    • VM → Threads → Fibers
  • After
    • VM → Guilds → Threads → Fibers
    • ネイティブなスレッドが並列動作するようになる

GC の処理が走ると、その間、全てのGuilds が止まる

これからやること

  • 機能
    • Prohibit
    • 同期
    • Shareable プロトコル
  • 最適化

Guild の名前について

プレフィックスを P (Process), T (Thread), F (Fiber) から避けた

問題点

"Move" オペレーションが一般的じゃない

mruby can be more lightweight

なぜ mruby が小さくないと駄目か

よくネット記事に載っているボードが 96K しか RAM がない

どうやるか

コンセプト

  1. define で mruby の機能を無効にし、RAM の使用量を減らす
  2. 実装をかえて ROM に置いて RAM を空ける

結果

  • default だと 153 KB のRAMを使う
  • 機能を無効にしていくと 83 KB
  • ROM に載せて 67 KB

どうやったか

  • 実行時に決まっているメソッド、Symbolを ROM に配置する
  • 実行中に追加されるメソッド、Symbolは RAM に置く
  • RAM 上のメソッドを探索し、なければ ROM から探すようにした
  • Hash のデフォルトのテーブルサイズを無理矢理小さくした

問題点

remove_method で ROM に配置されたメソッドを削除できない

所感

ガチで組み込みシステムな話だった。昔そういう業態にいたので懐かしかったが・・・。

Ruby Programming with Type Checking

Steep

  • Gradual typing for Ruby
    • コメントとしての型アノテーション
    • プログラマーによる型定義
    • ローカルの型推論

version 0.3.0を昨日リリースした

Sorbet との比較 f:id:Watson:20180601144452j:plain

デモ

デモのコード

1 + "2"

コマンド

$ steep check

以下のような型を定義したファイルを別途用意できる。

class Presentation
  @title: String
  @speaker: Speaker

  def initialize: (title: String, speaker: Speaker) -> any
  def print: (?_Puts) -> void
  def title: -> String
  def speaker: -> Speaker
end

型チェックのステップ

  1. シグネチャーを書く
  2. Ruby コードをアノテーションを付けて書く
  3. steep check
  4. 1. へ戻る

デモ

strictなチェックをしたい場合には steep check --strict とする

steep scaffold で型定義を自動生成できる

型定義の 40 % はライブラリのためだった。もし gem が型定義付きでリリースされれば、それを削減できる。

メタプログラミング

  • Rails みたいにメタプログラミング使いまくっているところの型チェックは大変そう
  • とりあえず、今のところ ActionArg 使ってくださいとのこと

RNode with code locations

  • アイコンは猫ではなく、フクロウです。
  • 来週からトレジャーデータにjoinしますとのこと。

2.5 で RNode を変え、コードレンジが増えている。 以下のケースで便利だから追加

  • 例外
  • warning
  • テストのカバレッジ

カラムとは。

  • 2.4 までは行番号しか保持していなかった
  • 2.5 からカラム情報も追加した
    • 行の先頭からどれくらい離れているかという情報
    • Byte length
    • (1,0) みたいな表現。 (行, カラム)

なぜ必要になったか

2.5 で追加されたブランチカバレッジとメソッドカバレッジで必要になった

  • ブランチカバレッジ → 分岐の片方をどれだけ実行したか。
  • メソッドカバレッジ

可視化する際にカラム情報が必要だった。

  • 3項演算子によりひとつの行は複数の分岐を書くことができる
  • 行番号しか無いと、どれが実行されたか判別できないため

Ruby スクリプトの Tokenize

$ ruby --dump=y -e '1 + 2' | grep Shifting
Shifting token tINTEGER (1.0-1.1: )
Shifting token '+' (1.2-1.3: )
Shifting token tINTEGER (1.4-1.5: )
Shifting token '\n' (1.5-1.5: )
Shifting token "end-of-input" (1.5-1.5: )

Parsing

$ ruby --dump=y -e '1 + 2'

Build AST

$ ruby --dump=p -e '1 + 2'
###########################################################
## Do NOT use this node dump for any purpose other than  ##
## debug and research.  Compatibility is not guaranteed. ##
###########################################################

# @ NODE_SCOPE (line: 1, code_range: (1,0)-(1,5))
# +- nd_tbl: (empty)
# +- nd_args:
# |   (null node)
# +- nd_body:
#     @ NODE_PRELUDE (line: 1, code_range: (1,0)-(1,5))
#     +- nd_head:
#     |   (null node)
#     +- nd_body:
#     |   @ NODE_OPCALL (line: 1, code_range: (1,0)-(1,5))
#     |   +- nd_mid: :+
#     |   +- nd_recv:
#     |   |   @ NODE_LIT (line: 1, code_range: (1,0)-(1,1))
#     |   |   +- nd_lit: 1
#     |   +- nd_args:
#     |       @ NODE_ARRAY (line: 1, code_range: (1,4)-(1,5))
#     |       +- nd_alen: 1
#     |       +- nd_head:
#     |       |   @ NODE_LIT (line: 1, code_range: (1,4)-(1,5))
#     |       |   +- nd_lit: 2
#     |       +- nd_next:
#     |           (null node)
#     +- nd_compile_option:
#         +- coverage_enabled: false

Compile

$ ruby --dump=i -e '1 + 2'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,5)>==============================
0000 putobject_OP_INT2FIX_O_1_C_                                      (   1)[Li]
0001 putobject        2
0003 opt_plus         <callinfo!mid:+, argc:1, ARGS_SIMPLE>, <callcache>
0006 leave

実装の話

トークン作ったときカラム情報を保持し、ISeq まで順に渡すような変更を地道にされたそう。偉業ですね

応用例

  1. Proc のコードロケーションを取得するメソッドが作れる
  2. NoMethodError のメッセージをより詳細にできる
  3. AST オブジェクトを取ることができる → Ruby 2.6 preview 2 に入りました

Type Profiler: An analysis to guess type signatures

Ruby 2.6 むけにエンドレスRange (1..) を実装した

ary[1..]
(1..).each { ...}
ary.zip(1..) { |x,i| ...}

既存の提案されている型システムについて

以下の既存の型システムを検証した

  1. Steep
  2. RDL
  3. contracts.ruby
  4. dry-types
  5. RubyTypeInference (by JetBrains)
  6. Sorbet (by Stripe)

RDL

  • アカデミックな世界では超有名
  • gem もある
  • 型アノテーションをコードとして書く必要があるそう

検証結果として、Steep みたいに型情報を Ruby とは別のファイルに書くのが Ruby 3 の要求仕様っぽい感じと受け止めている

Steep

静的な型検査。今日、発表があったのでスキップ

contracts

RDL みたいに型アノテーションをコードとして書く

RubyTypeInference

TracePoint を駆使してテストコードから動的に型情報を抽出しているそう

ここまでのまとめ

Steep がいまのところ一番好印象

Type Profile

今後整備していきたいようなものの話。

  • 型情報を推定するもの
  • 多少間違っていてもユーザが直してくれるようなゆるいものを想定
  • いろいろ実験・検証がまだ必要とのこと