ズンドコキヨシで動的型付言語のベンチマーク

# 実験

- ズンドコキヨシをもとの定義通りに50万回実行します．
- 標準出力する代わりに平均文字列長を計算します．
- 言語ごとに最も効率の良さそうな方法で書いてみます．もっと良い書き方があったら教えて下さい．

# コード

## JavaScript

`let`や`const`をループ内で使用すると大きなコストがかかるため`var`を使います．

```js
'use strict';
var total = 500000;
var length = 0;
for (var i = total; i--;) {
    var z = 0;
    do {
        length += 2;
        if (Math.random() < 0.5) {
            ++z;
        } else {
            if (z >= 4) break;
            z = 0;
        }
    } while (true);
}
console.log(length / total + 3);
```

## Lua

書いたことなかったけどLua初めて書いた．JIT無しのLuaのために一応`random = math.random`としてメンバ参照コストを削ってます．

```lua
total = 500000
length = 0
math.randomseed(os.time())
random = math.random
for i = 1, total do
    z = 0
    repeat
        length = length + 2
        if random() < 0.5 then
            z = z + 1
        else
            if z >= 4 then
                break
            end
            z = 0
        end
    until false
end
print(length / total + 3)
```

## Ruby

`Kernel#rand`や`Random#rand`より`Array#sample`の方が速いそうなのでこちらで代用します．

```ruby
i = total = 500000
length = 0
patterns = [nil, true]
while (i -= 1) >= 0
    z = 0
    begin
        length += 2
        if patterns.sample
            z += 1
        else
            break if z >= 4
            z = 0
        end
    end while true
end
puts length.to_f / total + 3
```

## PHP

`mt_getrandmax() / 2` の値をハードコーディングしています．

```php
<?php
$i = $total = 500000;
$length = 0;
while ($i--) {
    $z = 0;
    do {
        $length += 2;
        if (mt_rand() < 1073741824) {
            ++$z;
        } else {
            if ($z >= 4) break;
            $z = 0;
        }
    } while (true);
}
$out = $length / $total + 3;
echo "$out\n";
```

## Perl

ループ内で`my`を使用すると大きなコストがかかるためループ外で使用します．

```pl
use strict; use warnings;
my $total = 500000;
my $length = 0;
my $i = $total;
my $z;
while ($i--) {
    $z = 0;
    while (1) {
        $length += 2;
        if (rand() < 0.5) {
            ++$z;
        } else {
            if ($z >= 4) { last; }
            $z = 0;
        }
    }
}
my $out = $length / $total + 3;
print "$out\n";
```

## Python

```py:Python&#32;3
import random
total = 500000
length = 0
rand = random.random
for x in range(total):
    z = 0
    while True:
        length += 2
        if rand() < 0.5:
            z += 1
        else:
            if z >= 4:
                break
            z = 0
print(length / total + 3)
```

```py:Python&#32;2
import random
total = 500000
length = 0
rand = random.random
for x in xrange(total):
    z = 0
    while True:
        length += 2
        if rand() < 0.5:
            z += 1
        else:
            if z >= 4:
                break
            z = 0
print float(length) / total + 3
```

## R

最初グラフとか出すためにRだけで実現しようとしたら，致命的に遅かったので他の言語と速度を比べたくなった，というこの記事のきっかけとなった言語．

「Rはベクトル演算でやったほうが圧倒的に速い」という話は聞くけど，今回の処理においては`for`で書くしか無さそうだったので，仕方なく…

```R
total <- 500000
length <- 0
for (i in 1:total) {
    z <- 0
    repeat {
        length <- length + 2
        if (runif(1) < 0.5) {
            z <- z + 1
        } else {
            if (z >= 4) break
            z <- 0
        }
    }
}
write(length / total + 3, stdout())
```

# 結果

クリップボードから流し込んで実行します．優秀だった順に並べます．

```shell-session:Node.js&#32;5.7.1
mpyw@localhost:~$ time pbpaste | node
67.00084

real    0m0.275s
user    0m0.297s
sys     0m0.033s
```

```shell-session:LuaJIT&#32;2.0.4
mpyw@localhost:~$ time pbpaste | luajit
66.994236

real    0m0.324s
user    0m0.311s
sys     0m0.018s
```

```shell-session:Python&#32;2.7.10&#32;with&#32;PyPy&#32;4.0.1
mpyw@localhost:~$ time pbpaste | python
67.156712

real    0m0.737s
user    0m0.696s
sys     0m0.092s
```

```shell-session:Python&#32;3.2.5&#32;with&#32;PyPy3&#32;2.4.0
mpyw@localhost:~$ time pbpaste | python
66.965788

real    0m1.063s
user    0m1.017s
sys     0m0.094s
```

突出して速いJIT御三家です．但しPyPyはこの中では少し遅め．Python2のほうがPython3より速いことも特徴的です．

```shell-session:PHP&#32;7.0.1
mpyw@localhost:~$ time pbpaste | php
66.90856

real    0m1.028s
user    0m1.040s
sys     0m0.027s
```

```shell-session:HipHop&#32;VM&#32;3.12.1
mpyw@localhost:~$ time pbpaste | hhvm /dev/stdin
67.039644

real    0m1.298s
user    0m1.246s
sys     0m0.083s
```

爆速と言われるPHP7．**JIT無しの中ではPHP7が最速です．**JITで高速化を狙ったHHVMも相当速いですが，内部構造の刷新というアプローチを取ったPHP7のほうが速いのは興味深いです．PHP7にJITが載ればどうなるんでしょうか．

```shell-session:Ruby&#32;2.2.2
mpyw@localhost:~$ time pbpaste | ruby
67.006156

real    0m1.760s
user    0m1.728s
sys     0m0.081s
```

```shell-session:PHP&#32;5.5.30
mpyw@localhost:~$ time pbpaste | php
67.087172

real    0m1.721s
user    0m1.744s
sys     0m0.028s
```

RubyやPHP5もPHP7に次いで速いです．

```shell-session:Perl&#32;5.18.2
mpyw@localhost:~$ time pbpaste | perl
66.88662

real    0m2.612s
user    0m2.598s
sys     0m0.023s
```

```shell-session:Lua&#32;5.2.4
mpyw@localhost:~$ time pbpaste | lua
66.94308

real    0m2.627s
user    0m2.603s
sys     0m0.022s
```

JITが無くてもそこそこ速いLua． 歴史あるPerlも並んでます．

```shell-session:Python&#32;2.7.1
mpyw@localhost:~$ time pbpaste | python
67.098124

real    0m3.859s
user    0m3.846s
sys     0m0.032s
```

```shell-session:Python&#32;3.5.1
mpyw@localhost:~$ time pbpaste | python
66.947476

real    0m4.444s
user    0m4.370s
sys     0m0.105s
```

JIT無しでもPython2のほうがPython3より速いことは共通してますね．

```shell-session:R&#32;3.2.3
mpyw@localhost:~$ time pbpaste | RScript -
66.89728

real    1m0.795s
user    0m58.386s
sys     0m0.723s
```

Rはお察し

# 実行速度の関係

何をするかにも大きく左右されますが，あくまでズンドコキヨシるという前提で示します．JITを採用しているものは太字で示します．

1. **Node.js**<br />**LuaJIT**
2. **PyPy (Python 2 JIT)**
3. **PyPy (Python 3 JIT)**<br />PHP 7 
4. **HHVM (PHP 5 JIT)**
5. PHP 5<br />Ruby
6. Perl<br />Lua
7. Python 2<br />
8. Python 3<br />---宝くじで1億当たるぐらいの確率でしか超えられない壁---
9. R

こうなりました．PHPはメジャーバージョンアップで速くなったのになんでPythonは遅くなってんの…

# 番外編

VMの起動が計測時間に含まれていないので他のものと比較は出来ませんが，Webブラウザのコンソールでも計測してみました．すると，書き方によってかなりの差が現れました．

```js:A&#32;グローバルスコープで実行する
'use strict';
console.time('Time');
var total = 500000;
var length = 0;
for (var i = total; i--;) {
    var z = 0;
    do {
        length += 2;
        if (Math.random() < 0.5) {
            ++z;
        } else {
            if (z >= 4) break;
            z = 0;
        }
    } while (true);
}
console.log(length / total + 3);
console.timeEnd('Time');
```

```js:B&#32;即時関数スコープで実行する
'use strict';
console.time('Time');
console.log((function (global) {
    var total = 500000;
    var length = 0;
    for (var i = total; i--;) {
        var z = 0;
        do {
            length += 2;
            if (global.Math.random() < 0.5) {
                ++z;
            } else {
                if (z >= 4) break;
                z = 0;
            }
        } while (true);
    }
    return length / total + 3;
})(window));
console.timeEnd('Time');
```


<table>
<tr><th></th><th>Chrome 49</th><th>Firefox 45</th><th>Safari 8</th></tr>
<tr><th>A</th><td>0m30.283s</td><td>フリーズ</td><td>0m11.146s</td></tr>
<tr><th>B</th><td>0m0.700s</td><td>0m0.184s</td><td>0m0.495s</td></tr>
</table>

スコープ隠蔽しないとJITによる最適化が入らないっぽいですね．