« Hacking Ruby by Fridge | トップページ | おいたち »

Hacking Ruby by Fridge / 2

昨日の夜の続き.
結論から言うと,まつもとさんの言う通り関数ポインタじゃほとんど速度が上がらないことが判明.
以下その実証(あんま意味ないけど).

まつもとさんのコメント(昨日の日記参照)を見た後で 実際にrb_eval()の中の条件分岐を関数ポインタにする作業をやるのは 呪われそうでさすがにちょっと嫌だったので,インチキコードをうめこんで実験してみた.

昨日使ったベンチマークをデバッガの上で起動して眺めてると,rb_eval()上ではどうやら NODE_CALL,NODE_LVAR,NODE_LIT,NODE_NEWLINEの四つの型のノードが 主に使われているようなので,ソースに次のように追加.

      
2916  if(nd_type(node)==NODE_CALL)        goto _NODE_CALL;
2915  else if(nd_type(node)==NODE_LVAR)   goto _NODE_LVAR;
2916  else if(nd_type(node)==NODE_LIT)    goto _NODE_LIT;
2917  else if(nd_type(node)==NODE_NEWLINE)goto _NODE_NEWLINE;
2918  switch (nd_type(node)) {
2919    case NODE_BLOCK:
2920      if (contnode) {
...
3436    case NODE_CALL:
3437    _NODE_CALL:
...

上記みたいにcase文の下にラベルをはって,switch文に入る前に goto文を使って条件分岐の演算を前倒しする.
こうすればベンチマークで使われる4つタイプのノードがどんなにcase文で優先順位が低くても, 条件分岐演算は多くても4回(平均2回)ですむことになる.

で,このソースでベンチマークをまわせば取り合えず速くなると思ったんだけど全然速度変わらず. ということはボトルネックはやはり条件に到達した後の処理ということか.

さすがに優秀な人がソースを見てるだけあって,高速化も単純にはいかなかった.

---おまけ---
あんま推奨はされないけど,実は関数ポインタ以上に switch文のような条件分岐の速度を向上させる方法がある.
ほんとはrubyのソースコードにやってみようかと思ったけど 条件分岐が速度にあんま関係なさそうだったので今回は断念.

      3     static void* to[]={
      4          &&TO0,&&TO1,&&TO2,
      5     };
      6     goto *to[1];
      7     TO0:
      8     //operation 0
      9     TO1:
     10     //operation 1
     11     TO2:
     12     //operation 2
     13     return 0;
     14 }

&&演算子を使うとラベルが表すアドレスを取得できるので, これを利用して指定したアドレスにいきなりジャンプする.

単純にjmp命令に置き換わるだけなのでオーバーヘッドがなく速い(たぶん).

« Hacking Ruby by Fridge | トップページ | おいたち »

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/163977/3753386

この記事へのトラックバック一覧です: Hacking Ruby by Fridge / 2:

« Hacking Ruby by Fridge | トップページ | おいたち »