RubyからDの関数をつかう → はやい

#初めに(言い訳)
D言語Advent Calendarの記事なのにRuby主体の記事っぽくなってしまいましたが、あくまでもD言語が速いということを示したかったため、こういう記事になりました。

#きっかけ
D言語Advent Calendarに参加しようと思っていたけど、ネタが無かったので投稿するつもりはなかったのですが、ふとQiitaを漁っていた所以下のページに辿り着きました。

[RubyからGoの関数をつかう → はやい](http://qiita.com/grj_achm/items/679b3f3af2cf377f0f02)

そこで、Go言語で出来るんだからD言語でも出来るよ!ということを示したかったのでD言語でやってみた、というわけです。

#比較
とりあえず、D言語が高速であるということを示すためにフィボナッチ数列の第40項までを計算するアルゴリズムと竹内関数(tarai関数とし、以下に示すように定義される関数)に(13, 8, 0)を引数として渡した時の実行時間を比較してみます。

なお、筆者の環境はMacBook Pro Retina 13inch Mid 2014(Core i5  Dual Core 2.6GHz)にて
OSX 10.11.1
Ruby: 2.2.3p173 (2015-08-18 revision 51636) [x86_64-darwin15]
DMD: v2.069(64 bit)

###フィボナッチ数列の定義

```math
\begin{align}
F_0 &= 0 \\
F_1 &= 1 \\
F_{n+2} &= F_n + F_{n+1} \quad (n \ge 0) \\
\end{align}
```

###竹内関数の定義

```math
\begin{eqnarray}
Tarai(x,y,z)=\left\{ \begin{array}{ll}
y & if x \leqq y \\
Tarai(Tarai(x - 1, y, z), Tarai(y - 1, z, x), Tarai(z - 1 , x, y)) & otherwise. 
\end{array} \right.
\end{eqnarray}
```


次にコードを。
先にフィボナッチ数列の第40項を計算するコードで比較してみます。

```d:fib.d
import std.stdio;

uint fib(uint n){
  if(n <= 1){
    return n;
  }
  return fib(n - 1) + fib(n - 2);
}

void main(){
  fib(40).writeln;
}
```

```ruby:fib.rb
def fib(n)
  if n <= 1
    return n
  else
    return fib(n - 1) + fib(n - 2)
  end
end

puts fib(40)
```

##実行結果:
```zsh:
# D lang
alphakai@MacBook-Pro ~/tmp/dAdvent % dmd fib.d
alphakai@MacBook-Pro ~/tmp/dAdvent % time ./fib
102334155
./fib  0.89s user 0.00s system 99% cpu 0.900 total
# Ruby
alphakai@MacBook-Pro ~/tmp/dAdvent % time ruby fib.rb
102334155
ruby fib.rb  16.38s user 0.09s system 98% cpu 16.747 total
```

D言語の方がRubyよりも約18倍高速ですね。
続けて竹内関数による比較をしてみましょう。

```d:tarai.d
import std.stdio;

int tarai(int x, int y, int z){
    return
      x <= y ?
        y : tarai(tarai(x - 1, y, z),
                  tarai(y - 1, z, x),
                  tarai(z - 1, x, y));
}

void main(){
  tarai(13, 8, 0).writeln;
}
```

```ruby:tarai.rb
def tarai(x, y, z)
  x <= y ?
    y : tarai(tarai(x - 1, y, z),
              tarai(y - 1, z, x),
              tarai(z - 1, x, y))
end

puts tarai(13, 8, 0)
```

###実行結果:
```zsh
# D lang
alphakai@MacBook-Pro ~/tmp/dAdvent % dmd tarai.d
alphakai@MacBook-Pro ~/tmp/dAdvent % time ./tarai
13
./tarai  0.34s user 0.00s system 98% cpu 0.351 total
# Ruby
alphakai@MacBook-Pro ~/tmp/dAdvent % time ruby tarai.rb
13
ruby tarai.rb  5.93s user 0.04s system 97% cpu 6.097 total
```

こちらも約17倍D言語が高速であるという結果になりました。

それでは、Ruby側からD言語の関数を呼んでみましょう。
先ほど張ったURLと重複する内容ですが、RubyからD言語の関数を呼び出すにはFFIを用います。
`$ gem install ffi`
でインストールすることが出来ます。

D言語の関数をRubyから呼びだそうとする場合、そのままでは呼び出すことが来ません。
というのも、関数の呼び出し規約がD言語とC言語とでは違うためです。
ですので、`extern(C)`属性をつけることでFFIで呼び出すことが出来るようにします。

```d:fib_ffi_d.d
extern(C){
  uint fib(uint n){
    if(n <= 1){
      return n;
    }
    return fib(n - 1) + fib(n - 2);
  }
}
```

```ruby:d-fib.rb
require "ffi"

module Fib
  extend FFI::Library
  ffi_lib "fib_ffi_d.dylib"
  attach_function :fib, [:uint], :uint
end

puts Fib.fib(40)
```

###実行結果:
```zsh
alphakai@MacBook-Pro ~/tmp/dAdvent % dmd -shared fib_ffi_d.d
alphakai@MacBook-Pro ~/tmp/dAdvent % time ruby d-fib.rb
102334155
ruby d-fib.rb  1.15s user 0.03s system 92% cpu 1.285 total
```

先程は16.38sでしたので約14倍の高速化に成功しました。

続いて竹内関数での比較

```d:tarai_ffi_d.d
extern(C){
  int tarai(int x, int y, int z){
    return
      x <= y ?
        y : tarai(tarai(x - 1, y, z),
            tarai(y - 1, z, x),
            tarai(z - 1, x, y));
  }
}
```

```ruby:d-tarai.rb
require "ffi"

module Tarai
  extend FFI::Library
  ffi_lib "tarai_ffi_d.dylib"
  attach_function :tarai, [:int, :int, :int], :int
end

puts Tarai.tarai(13, 8, 0)
```

###実行結果:
```zsh
alphakai@MacBook-Pro ~/tmp/dAdvent % dmd -shared tarai_ffi_d.d
alphakai@MacBook-Pro ~/tmp/dAdvent % time ruby d-tarai.rb
13
ruby d-tarai.rb  0.47s user 0.02s system 98% cpu 0.498 total
```

先ほどは5.93sだったのでこちらも約12倍の高速化に成功しました。


実行環境によってどれほど高速化されるかは変わるかと思いますが、D言語が高速であるという事を端的に示すことが出来たのではないかと思います。


何かRubyで開発している時に高速化する必要が出てきたらそのロジックをD言語で書いてみるとよいかもしれませんね。