Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
で高速なプログラムを
書く
川崎 会議 基調講演
遠藤侑介
1
自己紹介:遠藤侑介
• Ruby コミッタ(2008年~)
– Rubyのテストを増強した
– コードカバレッジ測定機能を
実装した
– キーワード引数を実装した
– Ruby 2.0 リリースマネージャ
だった
– 最近は何もしてない
2
’...
と私
• 立ち上げの時に @chezou さんに相談を受けた
• 初期に数回だけ参加した
• Kawasaki.rb #005 (2013-10-23)で発表した
– 以上(すみません)
• ちなみに Kawasaki.rb #005 で発表し...
eval$s=%q(eval(%w(B=92.chr;N=10.chr;n=0;e=->(s){Q[Q[s,B],?"].gsub(N,B+?n)};E=->(s){'("'+e[s]+'")'};d=->(s,t=?")
{s.gsub(t)...
• とある Ruby プログラム
• 実行すると Scala プログラムが出力される
• 実行すると Scheme プログラムが出力される
• …
• 実行すると REXX プログラムが出力される
• 実行すると元の Ruby プログラムが出力...
の発表の影響
• 不足言語の指摘をもとに、
50から100言語に増強しました
• Docker に対応しました
• @MakeNowJust さん
PR ありがとう
• GitHubのスター数が
2800 から 5100 に
増えました
• G...
『超絶技巧プログラミングの世界』
• こんなプログラムや
実装方法の解説が
いっぱい載ってます
– 技術評論社より発売中
7
閑話休題
• 今日の主題:Rubyで高速なプログラムを書く方法
8
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby 処理系ベンチマーク
• 与太話
9
• Ruby開発チームの最近のスローガン
– “Ruby3 will be 3 times faster than Ruby2”
• 条件[Matz の RubyKaigi 2015 の発表より]
– 2020年ごろ
– 比較対象は Ruby ...
開発者の悩み
• あらゆるプログラムを 3 倍速くするのは困難
– 今の Ruby は(スクリプト言語にしては)すでに相当高速
– 高速化すべきプログラムが高速になるようにしたい
• 『手軽』に試せるベンチマークプログラムがない
– Ruby ...
手軽なベンチマーク
• あなたのプログラムを『手軽』なベンチマークにして
公開しよう!
– あなたのプログラムが速くなる(かもしれない)
– Ruby 開発に貢献した気になれる(かもしれない)
• 『手軽』とは?
– 『手軽』で面白くて(一応)...
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby 処理系ベンチマーク
• 与太話
13
とは
• Rubyで書かれたNES(ファミコン)エミュレータ
Demo
14
開発動機
• Ruby3x3を煽るベンチマーク候補
– Ruby 2.0 で 20 fps で動く  Ruby 3.0 で 60 fps?
– CPU律速で現実的なベンチマーク
– 最適化ニンジン(Optimization Carrot)
•...
開発経緯
• 10年前:実装を試みるも断念
– 当時はRubyもCPUも遅すぎ、NES解析情報もいまいち
• 2015/11/08:大江戸 Ruby 会議 04
– Ruby で NES ROM を作るフレームワーク burn を見る
– ht...
の関連研究
• Ruby でファミコンプログラミング
(takkaw, 2007)
– NES ROM でプレゼン
• Nario:MRI の段階的 GC のデモ
(authornari, 2008)
– マリオ風ゲームのもたつき現象で
リアル...
のアーキテクチャ
CPU GPU
Program ROM Bitmap ROM
Cartridge
NES
RAM
(2 kB)
VRAM
(2 kB)
control
read
read/write
read
render
read/writ...
エミュレーションのボトルネック
GPU
80%
CPU
10%
その他
10%
実行時間比率
19
の仕事
• 背景描画(最大のボトルネック、次ページ)
• スクロール
– VRAMは2画面分あり、1画面分を描画する
• スプライト
– 背景にキャラチップを重ねて書ける
– 衝突判定:0番スプライトを描画するとき CPU 割り込みする
• G...
背景描画
• ピクセルごとに以下を実行 (1秒あたり256 x 240 x 60 = 3.7M回)
1. そこにあるビットマップ番号をタイル配置データから同定
2. パレット配置データを読んでパレットを同定
3. ビットマップ番号に対応するビッ...
の仕事はとても大変
http://wiki.nesdev.com/w/index.php/File:Ntsc_timing.png
22
の仕事
• 「プログラムROMから命令を読み出して実行する」を
繰り返す
• CPU:MOS 6502
– 1.7 MHz
– レジスタ 8 ビット、アドレス空間 16 ビット
– 可変長命令(1 - 3 バイトくらい)
– 可変クロック(1 ...
の仕事
• 以下の波形を合成して出力する
– 矩形波 x 2
– 三角波
– ノイズ
– PCM
– 簡単に言えば 3 和音+ドラム
• 実装は面倒だが、ボトルネックにはならない
– 44100 Hz なら、1 秒間に 44100 x O(1)...
と の通信
• CPUから見てGPUとAPUはメモリに見える
– アドレス0x2000番地に書き込むとGPUへの情報送信
– アドレス0x2000番地から読み込むとGPUからの情報取得
25
アドレス 内容
0x0000..0x07ff 普通の...
マッパー
• ファミコンカートリッジは単なるソフトウェアではない
– 各ゲーム専用の回路が組み込まれている
– 発売されたゲームの数だけ回路がある
• ROM ファイルには回路の番号だけが書かれている
– マッパーと呼ばれる
• 実用的なエミュ...
基本的なエミュレーション方法
• GPU・CPU・APU の機能をソフトウェアで実装する
– メモリを整数の配列で表す
– 「メモリを読み出し、何か計算し、メモリに書き戻す」
を繰り返す
27
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot(NESエミュレータ)
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby 処理系ベンチマーク
• 与太話
28
最適化の心得
1. 目標値を設定せよ
2. ボトルネックをいじれ
3. アルゴリズム最適化を考えよ
4. 効果を検証せよ
29
目標値を設定せよ
• 最適化=コードを汚すこと
– 目標値を設定しないと際限なくコードを汚くしてしまう
• 目標値がないと「小さなことからコツコツと」をやりがち
– 大きなことからやれ
– 「わずかでも速くしたい」という煩悩は諸悪の根源
– 本...
ボトルネックをいじれ
• 「2 倍高速化」「10%高速化」どっちが良いか?
– 答え:不明(それがボトルネックかどうかによる)
• 計算時間10%の処理を2倍高速化全体の5%削減
• 計算時間80%の処理を10%高速化全体の8%削減
– ボ...
アルゴリズム最適化を考えよ
• Optcarrot の場合
– ナイーブに書いて 3 fps
– アルゴリズム・データ構造の改善で 20 fps (約 7 倍)
– メソッド展開とか汚いことやって 80 fps (約 4 倍)
• アルゴリズム...
効果を検証せよ
• ×「実行してみたら速かった」
– 測定のたびにブレがある
– 都合のいい結果を記憶に残して「速くなった」と信じてしまう
• 確証バイアス:仮説や信念を検証する際にそれを支持する情報ばか
りを集め、反証する情報を無視または集め...
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby 処理系ベンチマーク
• 与太話
34
• a sampling call-stack profiler for ruby 2.1+
– https://github.com/tmm1/stackprof
• 特徴
– オーバーヘッドがほとんどない
– 測定結果は正確ではない
• イ...
の使い方
1. 測りたい部分を以下で囲む
– Optcarrot は初期化後のメインループを囲んでいる
2. 実行する  stackprof.dump ファイルができる
3. stackprof stackprof.dump で結果を見る
–...
メソッド単位のプロファイル結果
37
$ stackprof --method "Optcarrot::PPU#render_pixel"¥
stackprof-cpu.dump
Optcarrot::PPU#render_pixel (/ho...
プロファイリングの命は可視化
38
$ stackprof --callgrind stackprof.dump ¥
> stackprof.callgrind
$ kcachegrind stackprof.callgrind
オブジェクト生成の計測
39
$ stackprof obj.dump
==================================
Mode: object(1)
Samples: 86283 (0.00% miss rate)
GC...
参考:
• Rubyの実装レベルでボトルネックを見るときに使う
• 評価器が時間の半分、インスタンス変数読み込みが
16% 、メソッド探索が 6% 占めているとわかる
– perf については“RubyKaigi 2015 kosaki key...
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby 処理系ベンチマーク
• 与太話
41
アルゴリズム改善:
• ナイーブな実装:CPUとGPUの
エミュレーションを
クロック単位で切り替える
– 簡単で正確だが切り替えコストがかかって遅い(3fps)
CPU step
step
step
step
step
step
step
s...
アルゴリズム改善:
• キャッチアップ法:
CPUがGPUを制御したら
切り替える
– 正確で高速(10fps)(実装は大変だけど頑張る)
CPU run
catchup
run
catchup
run
clock
GPU CPU attemp...
アルゴリズム改善: 実装
• ナイーブな実装:ピクセル単位でエミュレーションする
– ハードウェアと同じことをする
画像データ
タイル配置データ
パレット配置データ
VRAM
GPU2
1
3
4
この計算をピクセルごとにやるので遅い
(一回の...
アルゴリズム改善: 実装
• スクリーンを事前描画して、変更部分のみ更新する
タイル配置データ
パレット配置データ
VRAM
GPU
screen buffer
VRAMが書き換わった 変更部分のみ更新
フレームごとに
ビデオ出力
※この説明は...
• CPUはGPUやAPUをメモリ越しに制御する
• メモリアクセスのナイーブな実装:
– 毎回条件判定するのは遅い
– メモリマップはカートリッジによって変わるので
ハードコーディングはいけてない
def memory_read(addr)
...
• Method#[] と Array#[] が同名なのを利用したコード
– 普通のメモリアクセスは配列参照 2 回でいける
– GPU・APU制御の場合は Method#[] で対応メソッドが
呼び出される
@mem = []
@mem[0x...
その他の最適化
• アルゴリズム最適化でも漸近的計算量だけを見ない
– 例:Ruby でリングバッファを実装するより、長さ次第では
Array#rotate!を使った方が速いことも
• いろいろ事前計算する
• こんな感じで 20 fps を達...
ここから闇
• ここまではプログラムの綺麗さは比較的維持していた
• ここからは、プログラムを汚くして高速化する
– 普通のRubyプログラマがマネするべき手法ではありません
– Rubyインタプリタの高速化の余地を図る研究です
49
™ 手動メソッド展開
• メソッド呼び出しは遅いので展開する
(当然ボトルネックのみ)
while catchup?
inc_addr
end
while catchup?
@addr += 1
end
28 fps  40 fps
50
™ インスタンス変数のローカル変数化
• インスタンス変数アクセスが遅い
– ローカル変数に置き換える
while catchup?
@addr += 1
end
begin
addr = @addr
while catchup?
addr +...
• 典型的な実行パスを展開する
™ while catchup?
if can_be_fast?
# fast-path
do_A
do_B
do_C
@clock += 3
else
case @clock
when 1 then do_A
...
各 ™ の効果
29.4
40.3
46.6
62.7
68.8
83.2
0.0 20.0 40.0 60.0 80.0
base
method inlining
ivar localization
fastpath
misc
CPU mis...
実装上の工夫
• 手動と言っても本当に手動にはしない
– 正規表現を使って自動的に変換する
• コードのインデントを頼りにパースする
– コードのインデントがちゃんとしている前提
– コードチェッカ Rubocop を使って、
コードのインデン...
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby3x3 のためのベンチマーク
• 与太話
55
の前に、テスト用 の入手方法
• 有志がスクラッチから作ったROMがある
– http://www.romhacking.net/
– ベンチマーク用にはこちら(Lan master を使用)
• もう1つの手段:実際のカートリッジから抽出する...
自作した吸出し器
57
自作した吸出し器
※吸出しプログラムも Ruby で書かれている
(arduino_fermata gem を利用) 58
Arduino + Firmata
自作シールド
(「ホンコン」ベース)
カートリッジ
接触悪い
パスコンなし
ベンチマーク(アルゴリズム改善)
28.7
28.1
25.5
26.6
25.0
21.4
5.83
21.9
39.2
25.0
4.10
7.48
27.0
0.0287
0.0 10.0 20.0 30.0 40.0
trunk
rub...
ベンチマーク(+闇)
28.6
28.0
25.2
26.9
26.1
21.4
5.87
22.8
39.3
25.3
3.97
7.02
29.3
0.0285
84.0
82.9
78.2
79.6
68.1
64.0
1.46
69.0...
考察
• JRuby 9k が最速: “Deoptimization” が有望
– レアケースを無視して最適化 JIT コンパイルしておく
– レアケースが発生したら、そこだけ JIT コンパイルしなおす
 卜部さんが検討中?
https:/...
不完全な 実装との戦い
• mruby:
– require なし、module_function なし、 fiber なし、
モジュールのインクルードがなんか微妙
• topaz:
– String#tr や #% を使ったら異常終了すること...
ベンチマークプログラム
• コード生成含めて 5000 行以下
• 非GUIモードならライブラリ一切不要
– miniruby(MRI開発用の不完全なRuby実装)でも動く
– GUIモードでは ruby-ffi 経由でSDL2 を使う
• 基...
よく使われるベンチ:
• 30000行超
• セットアップ手順
– PostgreSQL の設定
– Redmine インストールと初期設定と初期データ登録
– Apache のインストールと設定
– Passenger のインストールと設定
...
『手軽』なベンチマーク
• Ruby開発者にとって『手軽』であることが重要
• インストールやセットアップが不要
– git clone だけとコマンド一発で
標準的な設定とデータセットが準備できると理想
• コマンドラインから ruby fo...
は適切なベンチマーク?
• 「遅いRubyには向いてないアプリでは?」
– もう Ruby はそこまで遅くない
– 「科学技術計算向き」とされる Python よりも
(コア自体は)速くなってきている
• 「にしても、エミュレータはRubyの目...
アジェンダ
• 背景:Ruby3x3
• 事例:Optcarrot
• 最適化の心得
• プロファイラの使い方
• 最適化の具体例
• Ruby 処理系ベンチマーク
• 与太話
67
プログラミング言語は道具?
• 「言語は道具。用途に応じて使い分けるべき。」
– 本当?
• 「言語は道具」?
– 自然言語は思考・意思を伝達する道具というが
– 思考・意思は言語と独立している?本当に?
• 「用途に応じて使い分けるべき」?
...
つの言語にこだわる つの理由
• 言語が「脳のキャッシュ」に載る
– リファレンスを見ずにプログラムできる
– ストレスなくプログラミングするために重要
– 多数の言語をこの状態にするのは(自分は)難しい
• 言語の「土地勘」が身につく
– そ...
注意
• 他言語を勉強しなくていいわけではない
– 見識は広げるべき
– ただ、「広く浅く」だけでは見えてこないものもある
• 何が何でも 1 つの言語で完結させる必要はない
– 工夫しまくってファミコンエミュレータが書けたが
– 工夫せずに実...
まとめ
• Rubyで高速プログラムを書く方法を紹介しました
– 背景:Ruby3x3
– 事例:Optcarrot
– 最適化の心得
– プロファイラの使い方
– 最適化の具体例
– Ruby 処理系ベンチマーク
– 与太話
71
Upcoming SlideShare
Loading in …5
×

Ruby で高速なプログラムを書く

27,937 views

Published on

川崎Ruby会議01 基調講演

Published in: Entertainment & Humor
  • Be the first to comment

Ruby で高速なプログラムを書く

  1. 1. で高速なプログラムを 書く 川崎 会議 基調講演 遠藤侑介 1
  2. 2. 自己紹介:遠藤侑介 • Ruby コミッタ(2008年~) – Rubyのテストを増強した – コードカバレッジ測定機能を 実装した – キーワード引数を実装した – Ruby 2.0 リリースマネージャ だった – 最近は何もしてない 2 ’06下 ’07上 ’07下 ’08上 60 70 80 90 100 coverage(%) 70% 85% C0カバレッジ遷移
  3. 3. と私 • 立ち上げの時に @chezou さんに相談を受けた • 初期に数回だけ参加した • Kawasaki.rb #005 (2013-10-23)で発表した – 以上(すみません) • ちなみに Kawasaki.rb #005 で発表したものは 3
  4. 4. eval$s=%q(eval(%w(B=92.chr;N=10.chr;n=0;e=->(s){Q[Q[s,B],?"].gsub(N,B+?n)};E=->(s){'("'+e[s]+'")'};d=->(s,t=?") {s.gsub(t){t+t}};Q=->(s,t=?$){s.gsub(t){B+$&}};puts(eval(%q(%(objectXQRXextendsXApp{H("#{e[%((displayX"#{e[%(Hf X%sX"#{Q[e["TranscriptXshow:X'#{d[%(putsX [regsub X-allX{. }X"#{Q[e[%[intXK(){sJXs=#{E[%(withXAda.Text_Io;pro cedureXqrXisXbeginXAda.Text_Io.P ut_Li ne(" #{d[ %(BEGINXH("#{d[%(BEGIN{s=#{E[%(forXbXinXSystem.Text .ASCIIEncoding().GetBytes(#{Q[ E[" # include<stdio.h>`nintXK(){puts#{E["#includ e<iostream>`nintXK(){s td::c o ut<<#{E[%(classXProgram{publicXstaticXvoidX Main(){System.Console .Wr ite(#{E[%((defnXf[lXr](if(>(count Xr)45)(lazy-seq(cons ( str"XXXX^""r"^"&")(fXl"")))(let[c( firstXl)](ifXc(f(ne xtXl)(if(=XcX^")(strXrXcXc) (strXrXc)))[(str"X XXX^""r"^".")]))))(doall(map X#(Hln(str"XXXXXX XX"%1))(lazy-cat["IDENT IFICATIONXDIVISI ON.""PROGRAM-ID.XQR.""PR OCEDUREXDIVISI ON."]#{(" console.log"+E[%((wr ite-line"#{Q [%(X:XAX."XXXXXXXXX"X;X:X BXAX."XWRITE(*,*)'"XA X;X:XCXBXTY PEX."X'"XCRX;X:XDXS"XprogramXQR"XC XS^"XHX^"(&"XCXS^"X# {e[%(pack ageXK;import("fmt";"sJs");funcXK(){fmt.Pr int("H^x27"+sJs.Re place("# {e[e[%(importXData.Char`nK=putStrLn$"procedure XK();write(^"DO,1 <-#"++s how(lengthXs)++fXsX1X0;f(x:t)iXc=letXv=foldl(^aXx- >a*2+(modXxX2))0 $takeX 8$iterate(flipXdivX2)$Data.Char.ordXxXin(ifXmodXiX4<1 then"PLEASE"els e"")+ +"DO,1SUB#"++showXi++"<-#"++show(mod(c-v)256)++"^^n"++fX t(i+1)v;f[]_X_ ="PL EASEREADOUT,1^^nPLEA SEGIVEUP^");end";s=#{E[%(.classXpublic XQR`n.superXja va/l ang/Object`n.methodX publicXstaticXK([Ljava/lang/SJ;)V`n.lim itXstackX2`ng ets taticXjava/lang/Syst e m/outXLjava/io/PrintStream;`nldcX"#{e[% (classXQR{pu bli cXstaticXvoidXK(SJ[] v){ SJXc[]=newXSJ[8000],y="",z=y,s="#{z=t= (0..r=q=126) .ma p{ |n|[n,[]]};a=[];%(@s =inte rnalXconstant[#{i=(s=%(PRX"#{Q["H"+E[% (all:`n`t@H fX% sX"#{e[%(.assemblyXt {}.meth odXstaticXvoidXMain(){.entrypointXldst r"#{e["varX u=re quire('u til');u.H('#import<stdio. h>^n');u.H( #{E[% (in tXK(){puts #{E["H_ sJ"+E["Hf"+E[ %(say"# {e["programXQR(output);begi nX#{([*%($_ ="#{s= %(<?phpXecho"#{Q [e["i ntXK(){write#{E ["qr: -write('#{Q[e[%(forXlXin#{E[ e[d[%(eval$ s=%q(#$s) )]]]}.split("^^n") :H( 'cat("sayX^^"'+l+ '^^ "^^n")'))],?']}'),nl,halt."]} ;returnX0;} "]]}"?>);(s+N*( -s.size%6)).byt e s.map{|n|"%07b"%n}. j oin.gsub(/.{6}/){|n|n=n.to_i(2 );((n/26*6+ n+19)%83+46).ch r}}";s|.|$n=ord$ &;substrXunpack(B8,ch r$n-($n<58?-6:$n<91?65:71)),2|e g;s/.{7}/0$ &/g;HXpackXB.le ngth,$_).scan(% r (([X.0-9A-Za-z]+)|( . ))).reverse.map{|a,b|(b) ? "s//chrX#{b .ord}/e":"s//#{ a}/"},"eval"] *"X xX").gsub(/.{1,25 5}/ ){|s|"write( ' # {s}');"}} end."]}"`nend`n) ]]]};returnX 0;}). trXB,?@]}.repla ce(/@ /g,SJ.fr o m CharCode (92)))"]}"callXv oidX[mscor lib]Sys tem.Console:: Write(s J)ret})]}")],/ [ X ^`t;"() {}`[`]]/]}`nBYE)) .size+1}X xXi8]c"#{s.g s u b(/[^` n"]/){B+"%02`x58" %$&.ord}}^00"declareX i32@put s(i8*)defineXi32@K(){star t:%0=cal l X i32@pu ts(i8*Xgetelementp trXinbounds([#{i}XxXi 8]*@s ,i32X0,i32X0))retXi32X0}).bytes{|n | r ,z=z[ n]||(a<<r;q<5624&&z [n]=[q+=1,[]];t[n])}; a<< r;t=[*43..123]-[64,*92..96];a.map{|n | t[n/7 5].chr+t[n%75].chr}* ""}";intXi=0,n=0,q=0, t ;for(;++n<126;)c[n]=""+(char)n;for(; i <s.le ngth();){t=s.charAt(i );q=q*75+t-t/64-t/92 *5-43;if(i++%2>0){y=q<n?c[q]:y;c[n+ + ]=z+ y.charAt(0);System.out .H(z=c[q]);q=0;}}}}) ]}"`ninvokevirtualXjava/io/PrintStr e am/H ln(Ljava/lang/SJ;)V`nre turn`n.endXmethod)+N]})].trXB,?@]}^x27^n","@","^^",- 1))})]} "XDU PXFORXS"X&A,&"XCXNE`x58TX S^"X&A)^",&"XCX0XDOXBX."X&char("XCOUNTX.X."X),&' "XCRXLOOP XS^" X&^"^""XCXS"XendXprogramXQ R"XCXAX."XSTOP"XCRXAX."XEND"XCRXBYEX;XDX), /([^"])/]}"))]).gsu b(/. +/){%((cons"DISPLAY"(f"#{e[$ &]}""")))}}["STOPXRUN."])))).trXB,?~ ]}.Replace("~","^^")); }}) ]};}"]};returnX0;}"]]}):HXjoin( ['+'forXiXinXrange(0,b)],"") +".>").trXB,?!]};gsub(/! /," ^^",s);HXs})]}")END)]}");endXqr;) ]};intXi,j;H( "moduleXQR;initialXbeginX");for( i=0;i<s.length;i++){H("$write(^"XXX") ;for(j=6;j>=0;j--)H((s[i]>>j)%2>0?" ^^t":"X");H("^^n^^t^^nXX^`");");}H("$disp lay(^"^^n^^n^");endXendmodule");returnX0 ;}].reverse],/[`[`]$]/]}"X^x60.&]k),?']}';cr"]]} ")]}")).gsubXB*8,?|]}".replaceAll("^^|","#{B*32 }"))})).gsub(/[HJK^`X]/){[:print,0,:tring,:main,B*2,0,B,?¥s][$&.ord%9]})))*"")#_buffer_for_future_bug_fixes_#_b ################### Quine Relay -- Copyright (c) 2013 Yusuke Endoh (@mametter), @hirekoke ##################)
  5. 5. • とある Ruby プログラム • 実行すると Scala プログラムが出力される • 実行すると Scheme プログラムが出力される • … • 実行すると REXX プログラムが出力される • 実行すると元の Ruby プログラムが出力される • ... というプログラム(50 段階の Multi-quine) • (多分)世界初、50言語を使用するプロジェクト • https://github.com/mame/quine-relay/tree/50
  6. 6. の発表の影響 • 不足言語の指摘をもとに、 50から100言語に増強しました • Docker に対応しました • @MakeNowJust さん PR ありがとう • GitHubのスター数が 2800 から 5100 に 増えました • GitHub 一千万リポジトリ中 上位 1000 件(0.01%)に入る 6
  7. 7. 『超絶技巧プログラミングの世界』 • こんなプログラムや 実装方法の解説が いっぱい載ってます – 技術評論社より発売中 7
  8. 8. 閑話休題 • 今日の主題:Rubyで高速なプログラムを書く方法 8
  9. 9. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby 処理系ベンチマーク • 与太話 9
  10. 10. • Ruby開発チームの最近のスローガン – “Ruby3 will be 3 times faster than Ruby2” • 条件[Matz の RubyKaigi 2015 の発表より] – 2020年ごろ – 比較対象は Ruby 2.0 – ベンチマークは開発チームが選ぶ • 小さいが人工的でないもの – 省メモリよりスピード重視 10 チート?
  11. 11. 開発者の悩み • あらゆるプログラムを 3 倍速くするのは困難 – 今の Ruby は(スクリプト言語にしては)すでに相当高速 – 高速化すべきプログラムが高速になるようにしたい • 『手軽』に試せるベンチマークプログラムがない – Ruby コア開発者 = C 言語マスター ≠ Ruby ヘビーユーザ – Rails のセットアップとか知らない 11
  12. 12. 手軽なベンチマーク • あなたのプログラムを『手軽』なベンチマークにして 公開しよう! – あなたのプログラムが速くなる(かもしれない) – Ruby 開発に貢献した気になれる(かもしれない) • 『手軽』とは? – 『手軽』で面白くて(一応)実用アプリである ベンチマークプログラム事例として Optcarrot を作りました 12
  13. 13. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby 処理系ベンチマーク • 与太話 13
  14. 14. とは • Rubyで書かれたNES(ファミコン)エミュレータ Demo 14
  15. 15. 開発動機 • Ruby3x3を煽るベンチマーク候補 – Ruby 2.0 で 20 fps で動く  Ruby 3.0 で 60 fps? – CPU律速で現実的なベンチマーク – 最適化ニンジン(Optimization Carrot) • もう1つの動機: Rubyで無理そうなことをやってみたかった – NESの解像度:256 x 240 ピクセル x 60 fps – ループ以外のタスクを 0.8 秒で? (256*240*60).times do |i| ary[0] = 0 end 0.2 秒 15
  16. 16. 開発経緯 • 10年前:実装を試みるも断念 – 当時はRubyもCPUも遅すぎ、NES解析情報もいまいち • 2015/11/08:大江戸 Ruby 会議 04 – Ruby で NES ROM を作るフレームワーク burn を見る – http://wiki.nesdev.com/ を発見 • 2015/12/11-13:RubyKaigi 2015 – Ruby3x3 を聞き、一通り実装して 3 fps 程度になる • 2015/12/29-31:冬休み前半に 20 fps 達成 • 2016/01/01-03:冬休み後半に 60 fps 達成 • 2016/04/01:公開 16
  17. 17. の関連研究 • Ruby でファミコンプログラミング (takkaw, 2007) – NES ROM でプレゼン • Nario:MRI の段階的 GC のデモ (authornari, 2008) – マリオ風ゲームのもたつき現象で リアルアイム性をデモした • Burn (remore, 2014) – Ruby で NES ROM を作れる フレームワーク 17
  18. 18. のアーキテクチャ CPU GPU Program ROM Bitmap ROM Cartridge NES RAM (2 kB) VRAM (2 kB) control read read/write read render read/write ※NES 業界ではGPUではなく PPU (Picture Processing Unit) と言うようです interrupt 18 APU
  19. 19. エミュレーションのボトルネック GPU 80% CPU 10% その他 10% 実行時間比率 19
  20. 20. の仕事 • 背景描画(最大のボトルネック、次ページ) • スクロール – VRAMは2画面分あり、1画面分を描画する • スプライト – 背景にキャラチップを重ねて書ける – 衝突判定:0番スプライトを描画するとき CPU 割り込みする • GPUはNES大勝利の立役者 – 同時期のアーケードと遜色ないグラフィック – 同時期の汎用機(数十万円)より良い(NES:1.5万円) 20
  21. 21. 背景描画 • ピクセルごとに以下を実行 (1秒あたり256 x 240 x 60 = 3.7M回) 1. そこにあるビットマップ番号をタイル配置データから同定 2. パレット配置データを読んでパレットを同定 3. ビットマップ番号に対応するビットマップデータを読み込む 4. 組み合わせてビデオ信号にして送る タイル配置データ パレット配置データ VRAM GPU2 1 3 4 描画対象 正確には8ピクセル(1バイト)単位で処理する 21 画像データ Cartridge
  22. 22. の仕事はとても大変 http://wiki.nesdev.com/w/index.php/File:Ntsc_timing.png 22
  23. 23. の仕事 • 「プログラムROMから命令を読み出して実行する」を 繰り返す • CPU:MOS 6502 – 1.7 MHz – レジスタ 8 ビット、アドレス空間 16 ビット – 可変長命令(1 - 3 バイトくらい) – 可変クロック(1 - 9 クロックくらい) – パイプラインなし – 大体秒間 30 万命令くらい処理する • メインメモリ:2 kB – ツイート約 5 つ分(UTF-8 日本語) 23
  24. 24. の仕事 • 以下の波形を合成して出力する – 矩形波 x 2 – 三角波 – ノイズ – PCM – 簡単に言えば 3 和音+ドラム • 実装は面倒だが、ボトルネックにはならない – 44100 Hz なら、1 秒間に 44100 x O(1) の処理でよい – GPU は 256x240x60 = 3686400 x O(1) の処理が必要 – ノイズ生成のみ若干重い 24
  25. 25. と の通信 • CPUから見てGPUとAPUはメモリに見える – アドレス0x2000番地に書き込むとGPUへの情報送信 – アドレス0x2000番地から読み込むとGPUからの情報取得 25 アドレス 内容 0x0000..0x07ff 普通のメモリ 0x2000..0x3fff GPU 0x4000..0x5fff APUとパッド 0x6000..0x7fff 拡張メモリ 0x8000..0xffff プログラムROM char *gpu = (char*)0x2000; *gpu = 0x01;
  26. 26. マッパー • ファミコンカートリッジは単なるソフトウェアではない – 各ゲーム専用の回路が組み込まれている – 発売されたゲームの数だけ回路がある • ROM ファイルには回路の番号だけが書かれている – マッパーと呼ばれる • 実用的なエミュレータは、 発売されたゲームの数だけある回路を すべて実装しないといけない – Optcarrot は基本的なマッパーしか対応していない 26
  27. 27. 基本的なエミュレーション方法 • GPU・CPU・APU の機能をソフトウェアで実装する – メモリを整数の配列で表す – 「メモリを読み出し、何か計算し、メモリに書き戻す」 を繰り返す 27
  28. 28. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot(NESエミュレータ) • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby 処理系ベンチマーク • 与太話 28
  29. 29. 最適化の心得 1. 目標値を設定せよ 2. ボトルネックをいじれ 3. アルゴリズム最適化を考えよ 4. 効果を検証せよ 29
  30. 30. 目標値を設定せよ • 最適化=コードを汚すこと – 目標値を設定しないと際限なくコードを汚くしてしまう • 目標値がないと「小さなことからコツコツと」をやりがち – 大きなことからやれ – 「わずかでも速くしたい」という煩悩は諸悪の根源 – 本当にとにかく速くしたいなら C 言語へどうぞ • 「XXまで速くすれば○○できる、しないとできない」と いう目標がよい – 開発効率・メンテナンス性・マーケティングなどから決める – 達成できるかどうかで価値がまったく変わるのが望ましい • NESの場合 60 fps 30
  31. 31. ボトルネックをいじれ • 「2 倍高速化」「10%高速化」どっちが良いか? – 答え:不明(それがボトルネックかどうかによる) • 計算時間10%の処理を2倍高速化全体の5%削減 • 計算時間80%の処理を10%高速化全体の8%削減 – ボトルネックの特定にはプロファイラを使う(後述) 31
  32. 32. アルゴリズム最適化を考えよ • Optcarrot の場合 – ナイーブに書いて 3 fps – アルゴリズム・データ構造の改善で 20 fps (約 7 倍) – メソッド展開とか汚いことやって 80 fps (約 4 倍) • アルゴリズム改善の方が寄与度が高い – アルゴリズムに工夫の余地がなさそうなエミュレータですら – メソッド展開などはわかりやすくて印象に残りやすいが、 通常はメンテナンス性を犠牲にするほどの効果はない 32
  33. 33. 効果を検証せよ • ×「実行してみたら速かった」 – 測定のたびにブレがある – 都合のいい結果を記憶に残して「速くなった」と信じてしまう • 確証バイアス:仮説や信念を検証する際にそれを支持する情報ばか りを集め、反証する情報を無視または集めようとしない傾向のこと • 今回:最適化前と後で 30 回ずつ実行時間を計測、 Welch の t 検定で 5% 有意差があることを確認した • 有意差が認められなかったら変更を捨てる  重要 – 頑張って実装した最適化が効果なかったとは認めたくない ものだが、Mottainai の精神は悪 33
  34. 34. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby 処理系ベンチマーク • 与太話 34
  35. 35. • a sampling call-stack profiler for ruby 2.1+ – https://github.com/tmm1/stackprof • 特徴 – オーバーヘッドがほとんどない – 測定結果は正確ではない • インタプリタを数ミリ秒おきに監視し、各タイミングでどのメソッドが 実行されているかカウントする(サンプリングプロファイラ) – 使うためにコードを書き換える必要がある • 対抗:ruby-prof – 使いやすい:ruby foo.rb を ruby-prof foo.rb にするだけ – 測定結果が正確:メソッドごとの呼び出し回数 – オーバーヘッドがヤバい 35
  36. 36. の使い方 1. 測りたい部分を以下で囲む – Optcarrot は初期化後のメインループを囲んでいる 2. 実行する  stackprof.dump ファイルができる 3. stackprof stackprof.dump で結果を見る – render_pixelが実行時間の20%を占めていることがわかる 36 TOTAL (pct) SAMPLES (pct) FRAME 274 (19.3%) 274 (19.3%) Optcarrot::PPU#render_pixel 160 (11.3%) 160 (11.3%) Optcarrot::PPU#wait_one_clock 106 (7.5%) 105 (7.4%) Optcarrot::CPU#fetch StackProf.run(mode: :cpu, out: "stackprof.dump") { # 測定したいコード }
  37. 37. メソッド単位のプロファイル結果 37 $ stackprof --method "Optcarrot::PPU#render_pixel"¥ stackprof-cpu.dump Optcarrot::PPU#render_pixel (/home/mame/work/optcarrot/lib/optcarrot/pp samples: 309 self (17.8%) / 309 total (17.8%) callers: 309 ( 100.0%) Optcarrot::PPU#main_loop code: | 803 | def render_pixel | 804 | if @any_show 186 (10.7%) / 186 (10.7%) | 805 | pixel = @bg_enable 17 (1.0%) / 17 (1.0%) | 806 | if @sp_active && ( | 807 | if pixel % 4 == | 808 | pixel = sprite | 809 | else | 810 | @sp_zero_hit = | 811 | pixel = sprite | 812 | end | 813 | end | 814 | else 5 (0.3%) / 5 (0.3%) | 815 | pixel = @scroll_ad
  38. 38. プロファイリングの命は可視化 38 $ stackprof --callgrind stackprof.dump ¥ > stackprof.callgrind $ kcachegrind stackprof.callgrind
  39. 39. オブジェクト生成の計測 39 $ stackprof obj.dump ================================== Mode: object(1) Samples: 86283 (0.00% miss rate) GC: 0 (0.00%) ================================== TOTAL (pct) SAMPLES (pct) FRAME 86278 (100.0%) 86278 (100.0%) Optcarrot::PPU#main_loop 86282 (100.0%) 4 (0.0%) Optcarrot::PPU#run 1 (0.0%) 1 (0.0%) Optcarrot::CPU#run 5 (0.0%) 0 (0.0%) Optcarrot::NES#step 5 (0.0%) 0 (0.0%) Optcarrot::NES#run 5 (0.0%) 0 (0.0%) <main> 5 (0.0%) 0 (0.0%) <main> StackProf.run(mode: :object, out: “obj.dump") { # 測定したいコード }
  40. 40. 参考: • Rubyの実装レベルでボトルネックを見るときに使う • 評価器が時間の半分、インスタンス変数読み込みが 16% 、メソッド探索が 6% 占めているとわかる – perf については“RubyKaigi 2015 kosaki keynote”で検索 40 51.76% ruby ruby [.] vm_exec_core 16.18% ruby ruby [.] vm_getivar.isra.99 5.89% ruby ruby [.] vm_search_method.isra.79 $ perf record ruby ... $ perf report
  41. 41. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby 処理系ベンチマーク • 与太話 41
  42. 42. アルゴリズム改善: • ナイーブな実装:CPUとGPUの エミュレーションを クロック単位で切り替える – 簡単で正確だが切り替えコストがかかって遅い(3fps) CPU step step step step step step step step step step step step step step step step clock GPU 42 loop do @cpu.step @gpu.step end
  43. 43. アルゴリズム改善: • キャッチアップ法: CPUがGPUを制御したら 切り替える – 正確で高速(10fps)(実装は大変だけど頑張る) CPU run catchup run catchup run clock GPU CPU attempts to control GPU 43 loop do clks = @cpu.run @gpu.catchup(clks) end
  44. 44. アルゴリズム改善: 実装 • ナイーブな実装:ピクセル単位でエミュレーションする – ハードウェアと同じことをする 画像データ タイル配置データ パレット配置データ VRAM GPU2 1 3 4 この計算をピクセルごとにやるので遅い (一回の計算は大したことないが、一秒間に256 x 240 x 60 回やる) 44 Cartridge
  45. 45. アルゴリズム改善: 実装 • スクリーンを事前描画して、変更部分のみ更新する タイル配置データ パレット配置データ VRAM GPU screen buffer VRAMが書き換わった 変更部分のみ更新 フレームごとに ビデオ出力 ※この説明はイメージです 45 画像データ Cartridge
  46. 46. • CPUはGPUやAPUをメモリ越しに制御する • メモリアクセスのナイーブな実装: – 毎回条件判定するのは遅い – メモリマップはカートリッジによって変わるので ハードコーディングはいけてない def memory_read(addr) case addr when 0x0000..0x07ff then @main_memory[addr] when 0x2000..0x3fff then @gpu.read(addr) ... end end 46
  47. 47. • Method#[] と Array#[] が同名なのを利用したコード – 普通のメモリアクセスは配列参照 2 回でいける – GPU・APU制御の場合は Method#[] で対応メソッドが 呼び出される @mem = [] @mem[0x0000] = @main_memory ... @mem[0x2000] = @gpu.method(:read) ... def memory_read(addr) @mem[addr][addr] end 47
  48. 48. その他の最適化 • アルゴリズム最適化でも漸近的計算量だけを見ない – 例:Ruby でリングバッファを実装するより、長さ次第では Array#rotate!を使った方が速いことも • いろいろ事前計算する • こんな感じで 20 fps を達成した – Intel® Core™ i7-4500U @ 2.40 GHz / Ubuntu 16.04 0x23C0 | (addr & 0x0C00) | (addr >> 4 & 0x0038) | (addr >> 2 & 0x0007) ARY[addr] 48
  49. 49. ここから闇 • ここまではプログラムの綺麗さは比較的維持していた • ここからは、プログラムを汚くして高速化する – 普通のRubyプログラマがマネするべき手法ではありません – Rubyインタプリタの高速化の余地を図る研究です 49
  50. 50. ™ 手動メソッド展開 • メソッド呼び出しは遅いので展開する (当然ボトルネックのみ) while catchup? inc_addr end while catchup? @addr += 1 end 28 fps  40 fps 50
  51. 51. ™ インスタンス変数のローカル変数化 • インスタンス変数アクセスが遅い – ローカル変数に置き換える while catchup? @addr += 1 end begin addr = @addr while catchup? addr += 1 end ensure @addr = addr end 40 fps  47 fps 51
  52. 52. • 典型的な実行パスを展開する ™ while catchup? if can_be_fast? # fast-path do_A do_B do_C @clock += 3 else case @clock when 1 then do_A when 2 then do_B when 3 then do_C ... end @clock += 1 end end while catchup? case @clock when 1 then do_A when 2 then do_B when 3 then do_C ... end @clock += 1 end 47 fps  63 fps 52
  53. 53. 各 ™ の効果 29.4 40.3 46.6 62.7 68.8 83.2 0.0 20.0 40.0 60.0 80.0 base method inlining ivar localization fastpath misc CPU misc ProTip™ 1 ProTip™ 2 ProTip™ 3 53
  54. 54. 実装上の工夫 • 手動と言っても本当に手動にはしない – 正規表現を使って自動的に変換する • コードのインデントを頼りにパースする – コードのインデントがちゃんとしている前提 – コードチェッカ Rubocop を使って、 コードのインデントが変になっていないことを検証する src = File.read(__FILE__) src.gsub!(/.../) { ... } # method inlining src.gsub!(/.../) { ... } # ivar localization eval(src) 54
  55. 55. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby3x3 のためのベンチマーク • 与太話 55
  56. 56. の前に、テスト用 の入手方法 • 有志がスクラッチから作ったROMがある – http://www.romhacking.net/ – ベンチマーク用にはこちら(Lan master を使用) • もう1つの手段:実際のカートリッジから抽出する – このための回路を通称で「吸出し器」と呼ぶ 56
  57. 57. 自作した吸出し器 57
  58. 58. 自作した吸出し器 ※吸出しプログラムも Ruby で書かれている (arduino_fermata gem を利用) 58 Arduino + Firmata 自作シールド (「ホンコン」ベース) カートリッジ 接触悪い パスコンなし
  59. 59. ベンチマーク(アルゴリズム改善) 28.7 28.1 25.5 26.6 25.0 21.4 5.83 21.9 39.2 25.0 4.10 7.48 27.0 0.0287 0.0 10.0 20.0 30.0 40.0 trunk ruby23 ruby22 ruby21 ruby20 ruby193 ruby187 omrpreview jruby9k jruby17 rubinius mruby topaz opal 59 MRI は改善されている (1.81.92.02.3) OMR は速くない? (MRI 2.2 w/ JIT) JRuby9k が最速 ruby 2.0 で >20 fps (Ruby3x3 的に重要) こんなのでも動く
  60. 60. ベンチマーク(+闇) 28.6 28.0 25.2 26.9 26.1 21.4 5.87 22.8 39.3 25.3 3.97 7.02 29.3 0.0285 84.0 82.9 78.2 79.6 68.1 64.0 1.46 69.0 2.12 6.13 2.43 0.754 0.0501 0.0 10.0 20.0 30.0 40.0 50.0 60.0 70.0 80.0 90.0 trunk ruby23 ruby22 ruby21 ruby20 ruby193 ruby187 omrpreview jruby9k jruby17 rubinius mruby topaz opal default mode optimized mode 生成したソースコードは JVM 64k バイトコード制限に ひっかかるので JIT できない とのこと 60
  61. 61. 考察 • JRuby 9k が最速: “Deoptimization” が有望 – レアケースを無視して最適化 JIT コンパイルしておく – レアケースが発生したら、そこだけ JIT コンパイルしなおす  卜部さんが検討中? https://github.com/shyouhei/ruby/tree/deoptimization_base • OMR はあまり速くない? – JIT は望み薄? • perf によると 50% が評価器だが、これは基本的な組み込みメソッド の処理を含んでいる – OMR はできたばかり • チューニングの余地はあるし、opt_case_dispatch (case 文)の 最適化がまだっぽい 61
  62. 62. 不完全な 実装との戦い • mruby: – require なし、module_function なし、 fiber なし、 モジュールのインクルードがなんか微妙 • topaz: – String#tr や #% を使ったら異常終了することがある • opal: – パスが動的な require 禁止、binread を自力実装 • 適当な shim を作った – shim 不要なのは MRI と JRuby 9.1.0.0 だけ 62
  63. 63. ベンチマークプログラム • コード生成含めて 5000 行以下 • 非GUIモードならライブラリ一切不要 – miniruby(MRI開発用の不完全なRuby実装)でも動く – GUIモードでは ruby-ffi 経由でSDL2 を使う • 基本的な Ruby 機能しか使っていない – ruby 1.8 / mruby / topaz / opal でも shim などで動く • Ruby開発者がRubyの高速化を検証する流れ – miniruby ビルド→ miniruby 実行 63
  64. 64. よく使われるベンチ: • 30000行超 • セットアップ手順 – PostgreSQL の設定 – Redmine インストールと初期設定と初期データ登録 – Apache のインストールと設定 – Passenger のインストールと設定 • Ruby開発者がRubyの高速化を検証する流れ – Ruby 全ビルド→インストール→(Apache再起動?)→ ab 実行 64
  65. 65. 『手軽』なベンチマーク • Ruby開発者にとって『手軽』であることが重要 • インストールやセットアップが不要 – git clone だけとコマンド一発で 標準的な設定とデータセットが準備できると理想 • コマンドラインから ruby foo.rb で起動・終了する – 「このコマンドの実行時間を短縮せよ」だとわかりやすい • 数秒くらいで終わり、毎回の実行時間が安定しているとより良い – rake や rack 経由で実行するのはめんどい • プロファイラやデバッガが使いにくい • Ruby 開発者の Ruby は /usr/bin/ruby にはない • 依存関係が少ないこと、そんなに大きくないこと、など – miniruby で動くと理想(拡張ライブラリなし) 65
  66. 66. は適切なベンチマーク? • 「遅いRubyには向いてないアプリでは?」 – もう Ruby はそこまで遅くない – 「科学技術計算向き」とされる Python よりも (コア自体は)速くなってきている • 「にしても、エミュレータはRubyの目指すところか?」 – 個人的にはこういうのも書ける言語になってほしい – 他のものを高速化してほしい人は、自分なりの ベンチマークプログラムを作って公開してください 66
  67. 67. アジェンダ • 背景:Ruby3x3 • 事例:Optcarrot • 最適化の心得 • プロファイラの使い方 • 最適化の具体例 • Ruby 処理系ベンチマーク • 与太話 67
  68. 68. プログラミング言語は道具? • 「言語は道具。用途に応じて使い分けるべき。」 – 本当? • 「言語は道具」? – 自然言語は思考・意思を伝達する道具というが – 思考・意思は言語と独立している?本当に? • 「用途に応じて使い分けるべき」? – 言語の習得はコストが高い – 適応力がある人 or ニワカな人 or コードを書かない人 68
  69. 69. つの言語にこだわる つの理由 • 言語が「脳のキャッシュ」に載る – リファレンスを見ずにプログラムできる – ストレスなくプログラミングするために重要 – 多数の言語をこの状態にするのは(自分は)難しい • 言語の「土地勘」が身につく – その言語「らしい」書き方がわかる – 「らしい」書き方だと、効率的なプログラムになったり、 バージョンアップ時のハマりを減らせたりする – 言語機能の熟練度が予想できる(akr プロダクトは安心) • 言語の「守備範囲」がよくわかる – その言語でできることは思ったより広い 69
  70. 70. 注意 • 他言語を勉強しなくていいわけではない – 見識は広げるべき – ただ、「広く浅く」だけでは見えてこないものもある • 何が何でも 1 つの言語で完結させる必要はない – 工夫しまくってファミコンエミュレータが書けたが – 工夫せずに実速が出せる C 言語の方が適切なのは確か – ただ、ちょっとした工夫で言語を変えせずに済む場合も多い • エコシステムの存在はでかい – 科学技術計算やるなら Python に戦ってもしょうがない – 再発明のコストが新言語習得のコストを明らかに上回る 70
  71. 71. まとめ • Rubyで高速プログラムを書く方法を紹介しました – 背景:Ruby3x3 – 事例:Optcarrot – 最適化の心得 – プロファイラの使い方 – 最適化の具体例 – Ruby 処理系ベンチマーク – 与太話 71

×