C言語で書いた関数を Emacsから呼び出したら速いかどうか検証してみた.
この機能は Emacs 25で搭載される予定の Dynamic module機能を使って実現しています. Dynamic module機能については以下の記事を参考にしてみてください
リポジトリ
比較対象
フィボナッチ数の計算を以下のそれぞれで実装しました. C言語版 GCC 5.2.1でコンパイルし, 最適化オプションは -O2
です. Emacs Lisp版は Emacs 25.0.50.1でバイトコンパイルしたものを利用しました.
- Emacs Lisp再帰版
- Emacs Lispループ版
- C再帰版
- Cループ版
(defun fib-elisp (n)
(if (<= n 1)
n
(+ (fib-elisp (1- n)) (fib-elisp (- n 2)))))
(defun fib-elisp-loop (n)
(cl-loop for a = 0 then b
and b = 1 then (+ a b)
repeat n
finally return a))
static intmax_t
fib(intmax_t n)
{
if (n <= 1)
return n;
return fib(n - 1) + fib(n - 2);
}
static intmax_t
fib_loop(intmax_t n)
{
int a = 0, b = 1;
for (int i = 0; i < n; ++i) {
int tmp = a;
a = b;
b = tmp + b;
}
return a;
}
ベンチマーク
環境
- Ubuntu 15.10 x86_64 64bit
- CPU: Intel(R) Core(TM) i7-2620M CPU @ 2.70GHz
- Memory: 8GB
- Emacs: 25.0.50.1(2015年 12月 25日ビルド)
- GCC version 5.2.1 20151010 (Ubuntu 5.2.1-22ubuntu2) 最適化オプション
-O2
のみ
ベンチマークコード
以下のベンチマーク関数を n = 30
, count=100
で実行しました. 表示されるものは 100回実行したときの実行時間になります.
(defun fib-bench (n count)
(interactive
(list (read-number "Fibonacci number: ")
(read-number "Benchmark count: ")))
(benchmark count `(fib-elisp ,n))
(benchmark count `(fib-elisp-loop ,n))
(benchmark count `(fib-c ,n))
(benchmark count `(fib-c-loop ,n)))
(fib-bench 30 100)
結果
実行時間(秒) | 比率(EmacsLisp再帰版との) | |
---|---|---|
Emacs Lisp再帰 | 58.033767 | 1 |
Emacs Lispループ | 0.001959 | 29624 |
C再帰 | 0.338148 | 172 |
Cループ | 0.000044 | 1318949 |
題材が悪いというのもあるけど, Emacs Lispもアルゴリズムを適切に選べばそこまで遅くないという印象. 再帰版は論外レベルで遅いが.... Cでちゃんと書けば最も速そうだが, そこまで Cで書くのにこだわる必要もないように思えます. まだいろいろ試せてなさすぎなので, もっといろいろ見ていく必要はありますが...
その他
parsonという JSONパーサのバインディングを書いて見ました. これと標準パッケージの json.elとベンチマークを行ってみたのですが, 短い入力だと decodeはともかく encodeは遅いという結果が得られました. 大きめの入力だと両方とも Cが速いという結果が得られたのですが, 見極めにはもう少し時間がかかりそうです.
おわりに
本当は golangで使った shared libraryでいろいろ試すということを検討していたのですが, golangのランタイムで Segmentation faultが出る問題が解決できなかったのでこのような簡易な内容になってしまったことをお詫び申し上げます.
また mrubyネタの拡張を行おうかとも思っていたのですが, 現状の私の mrubyの知識が乏しすぎて実現することができませんでした. これはお正月に勉強してなんとかしようと思いますが, こちらについても合わせてお詫び申し上げます.