明日使える超高速ruby - rxbyak (mitaka.rb #5)
DESCRIPTION
Rubyのままで速くなる方法TRANSCRIPT
明日使える超高速 Ruby~ Ruby で Xbyak ~
2009/10/22
id:n_shuyo / @shuyo
中谷 秀洋@サイボウズ・ラボ
最近のマイブーム
機械学習自然言語処理
なんかこんな本
• Introduction to Information Retreival(IIR)
• パターン認識と機械学習(PRML, ぷるむる)
• とか
なんかこんなやつ
• ロジスティック回帰
• パーセプトロン
• ニューラルネットワーク
• サポートベクトルマシン(そのうち)
読んでるだけではよくわからない
• めっちゃ数式いっぱい
– 関数解析/線形代数/確率分布
– 最適化(プログラムのそれとは違うよ!)
– ラグランジュ未定乗数法
• よく知らない概念いっぱい
– ベイズ理論
– 回帰分析
– 確率過程
– カーネル法
実装してみるとわかる (こともある)
Rubyで実装
• これが間違い(><
Rubyはつらいよ……
MNISTデータセット(手書き数字)
• ニューラルネットワークによる学習– 入力 : 28x28(ピクセル)
– 中間層 : 300
– 出力 : 10 (0~9 の判別)
– 枝の本数 W = 27万
• 6万件の学習(一周)に24時間– 計算量が O(W^2)!
– Rubyの範囲での最適化はやりつくしたつもり• もちろん Ruby1.9
Rubyにもいいところはある
• ネットワーク構造をDSL で表現!
– ネットワークを変えての実験が容易
# unitsin_units = [Unit.new("x1"), Unit.new("x2")]hiddenunits = (1..6).map{|i| TanhUnit.new("z1#{i}")}out_unit = [SigUnit.new("y1")]
# networknetwork = Network.new(:error_func=>ErrorFunction::CrossEntropy)
network.in = in_units # 入力network.link in_units, hiddenunits # 入力 → 隠れユニットnetwork.link hiddenunits, out_unit # 隠れユニット → 出力network.out = out_unit # 出力
そうは言っても、速さは正義
• 少なくとも何百回とか学習が必要
– パラメータを変えながら交差検定とかしたい
• やっぱりC++で実装するか……
– Rubyの100倍~1000倍違うし
• 24時間/100 = 15分
– Rubyのお手軽さは捨てがたくもある
• C++はいちいちコンパイル
– C++に逃げたらなんか負けた気がする
• naoya_tさん曰く「リニアに速くなるだけでしょ?」
Rubyのままで速くなる方法はないのか?
RubyでXbyak
Xbyak(カイビャック)
• 実行バイナリ(ネイティブコード)を動的に生成するC++ライブラリ
• つまり JIT (Just in Time compiler)
• C++より速い
– C++のライブラリなのに!?
– もちろんRubyなんかメじゃない
• 光成さん(サイボウズ・ラボ)開発– http://homepage1.nifty.com/herumi/soft/xbyak.html
JITってなんだ?
JIT=MSXべーしっ君
要するに「めっっちゃ速い」
Ruby でXbyak が使えたらいいんじゃね?
RXbyak (Xbyak for Ruby)
• Xbyakを使えるRuby拡張
• Xbyak のネイティブコード生成機能を Rubyからそのまま利用可能に
• 生成したコードをRubyから呼び出す
– 実装率低め
– 明日まだ使えない!(><
http://github.com/shuyo/cpp/tree/master/rxbyak/
ひとことでいうと
RXbyak とは
Rubyでアセンブラが書ける
RXbyak サンプル
• SIMDを使って倍精度の掛け算
– どこからどうみても Ruby のプログラム。
rx = RXbyak.newrx.mov :eax, [:esp, 8] # mov eax, (esp+8) // 第1引数のポインタrx.movq :xmm0, [:eax] # movq xmm0, (eax)rx.mov :eax, [:esp, 12] # mov eax, (esp+12) // 第2引数のポインタrx.movq :xmm1, [:eax] # movq xmm1, (eax)rx.mulsd :xmm0, :xmm1 # mulsd xmm0, xmm1 // かけ算rx.mov :eax, [:esp, 4] # mov eax, (esp+4) // 返値のポインタrx.movq [:eax], :xmm0 # movq (eax), xmm0rx.ret # ret
puts rx.call(256.0, 256.0) # => 65536.0puts rx.call(123.45, 678.9) # => 83810.205
※このサンプルに使われている命令しかまだ実装されていません。
機械学習をRXbyak で書いたら
「××倍速くなったよ!」
まにあわんかったすまん(汗
(参考) Python vs Xbyak
http://labs.cybozu.co.jp/blog/mitsunari/2007/08/ll2007.html
何に使えるの?
RXbyakの応用範囲
• 実行時に処理が決まる場合(JIT)
– ニューラルネットワークはグラフが与えられれば
– URI Templates はテンプレートが与えられれば
• 参考:URI Templates のC++(Xbyak)実装• http://labs.cybozu.co.jp/blog/nakatani/2008/07/uri_template_c_xbyak_jit.html
– 正規表現
• プロセッサを極限まで使い切る!(アセンブラ)
– SIMD(SSE) で浮動小数演算
• 並列演算とか128bit精度とか。SSE4.1 なら内積も1命令で!
※使用者の感想であり、効用を約束するものではありません。
本日Mitaka.rb に参加した皆さんは
これでもう
「RXbyak で高速Rubyコード書いちゃうぞ~」
明日からビュンビュンですね!
※2009/10/22現在、mov(代入) と mul(掛け算) と ret しか実装されていません。
ありがとうございました