rubyでスタック・キューを作る実装をbddで進める課題の練習
せっかくふるまいを定義したので、c拡張とpure rubyで同じものを作成し、ベンチマークをとった
課題についてはbdd-stackを参照
$ ruby bench/bench.rb
- スタック
user | system | total | real | |
---|---|---|---|---|
LIFO (c ext) | 0.150330 | 0.002457 | 0.152787 | 0.153376 |
LIFO (ruby) | 0.044496 | 0.001956 | 0.046452 | 0.046825 |
- キュー
user | system | total | real | |
---|---|---|---|---|
FIFO (c ext) | 0.152509 | 0.002391 | 0.154900 | 0.155512 |
FIFO (ruby) | 0.050082 | 0.000594 | 0.050676 | 0.051421 |
データ挿入と取り出しをそれぞれ100,000回連続で行い比較した
-
スピードはrubyの方が約3〜4倍速い
-
ソースの行数はcが90〜100行に対して、rubyは35行〜50行で半分以下。
rubyが圧倒的にコンパクトに書ける。しかも雑につくったcより全然速い。
_人人人人人人人人人人人人人_
> C Launguage is dead <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
-
cからrubyオブジェクトを生成・操作しているだけなので、C言語の良さが全く生きない
-
リストを持つのにクラスをつくるのではなく、VALUEのアドレスを保持するような素の構造体を使うと早くなる(ような気がする)
-
これをやるとGCにVALUEを持っていかれそうな気がするので、もうちょっと挙動を理解したい
rubyのオブジェクトを生成・操作するコストがボトルネックだろうと踏んで、ネイティブなc構造体で実装してみた。
3番目がc構造体による実装結果。
user | system | total | real | |
---|---|---|---|---|
LIFO (c ext) | 0.150330 | 0.002457 | 0.152787 | 0.153376 |
LIFO (ruby) | 0.044496 | 0.001956 | 0.046452 | 0.046825 |
LIFO (c struct) | 0.027673 | 0.002248 | 0.029921 | 0.030232 |
かろうじて2倍弱のパフォーマンスが出ました。
_人人人人人人人人人人人人人_
> C Launguage is alive <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y ̄
この程度だとJITが入ったら大差なくなるのではという予感...
なお、以下のように実戦には耐えない残念な仕様です。
-
グローバル変数。全てのインスタンスが1つのスタックを指している
-
rubyオブジェクトをスタックに積む時に、GC対策として一応マーキングした。(pop時にアンマークできるかわからないがたぶんメモリリークする)
-
スレッドセーフかどうかなんて1mmも考慮されてない
-
頑張ってvimだけで開発してみたが開発時間と脳ミソの半分をvimに捧げることになった
-
コンパイルが通るまで時間がかかった。
init_xxxx
の関数名とextconf.rb
の指定の不一致(大文字・小文字の違い)でハマる -
queueを単方向リストで表現するのはちょっと面倒。双方向リストの方がよかったか
そもそもスタックを書いてみようという気になったのは@yalab師のお導きによります。 また資料としては、たまたま取り組んでいた神資料を盛大に読み返しました。 これがなかったら不可能でした(心で合掌)