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

  • 26
    Like
  • 13
    Comment
More than 1 year has passed since last update.

実験

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

コード

JavaScript

letconstをループ内で使用すると大きなコストがかかるためvarを使います.

'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としてメンバ参照コストを削ってます.

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#randRandom#randよりArray#sampleの方が速いそうなのでこちらで代用します.

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
$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を使用すると大きなコストがかかるためループ外で使用します.

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

Python 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)
Python 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で書くしか無さそうだったので,仕方なく…

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())

結果

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

Node.js 5.7.1
mpyw@localhost:~$ time pbpaste | node
67.00084

real    0m0.275s
user    0m0.297s
sys     0m0.033s
LuaJIT 2.0.4
mpyw@localhost:~$ time pbpaste | luajit
66.994236

real    0m0.324s
user    0m0.311s
sys     0m0.018s
Python 2.7.10 with PyPy 4.0.1
mpyw@localhost:~$ time pbpaste | python
67.156712

real    0m0.737s
user    0m0.696s
sys     0m0.092s
Python 3.2.5 with PyPy3 2.4.0
mpyw@localhost:~$ time pbpaste | python
66.965788

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

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

PHP 7.0.1
mpyw@localhost:~$ time pbpaste | php
66.90856

real    0m1.028s
user    0m1.040s
sys     0m0.027s
HipHop VM 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が載ればどうなるんでしょうか.

Ruby 2.2.2
mpyw@localhost:~$ time pbpaste | ruby
67.006156

real    0m1.760s
user    0m1.728s
sys     0m0.081s
PHP 5.5.30
mpyw@localhost:~$ time pbpaste | php
67.087172

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

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

Perl 5.18.2
mpyw@localhost:~$ time pbpaste | perl
66.88662

real    0m2.612s
user    0m2.598s
sys     0m0.023s
Lua 5.2.4
mpyw@localhost:~$ time pbpaste | lua
66.94308

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

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

Python 2.7.1
mpyw@localhost:~$ time pbpaste | python
67.098124

real    0m3.859s
user    0m3.846s
sys     0m0.032s
Python 3.5.1
mpyw@localhost:~$ time pbpaste | python
66.947476

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

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

R 3.2.3
mpyw@localhost:~$ time pbpaste | RScript -
66.89728

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

Rはお察し

実行速度の関係

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

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

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

番外編

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

A グローバルスコープで実行する
'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');
B 即時関数スコープで実行する
'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');
Chrome 49 Firefox 45 Safari 8
A 0m30.283s フリーズ 0m11.146s
B 0m0.700s 0m0.184s 0m0.495s

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