実験
- ズンドコキヨシをもとの定義通りに50万回実行します.
- 標準出力する代わりに平均文字列長を計算します.
- 言語ごとに最も効率の良さそうな方法で書いてみます.もっと良い書き方があったら教えて下さい.
コード
JavaScript
let
やconst
をループ内で使用すると大きなコストがかかるため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#rand
やRandom#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
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)
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())
結果
クリップボードから流し込んで実行します.優秀だった順に並べます.
mpyw@localhost:~$ time pbpaste | node
67.00084
real 0m0.275s
user 0m0.297s
sys 0m0.033s
mpyw@localhost:~$ time pbpaste | luajit
66.994236
real 0m0.324s
user 0m0.311s
sys 0m0.018s
mpyw@localhost:~$ time pbpaste | python
67.156712
real 0m0.737s
user 0m0.696s
sys 0m0.092s
mpyw@localhost:~$ time pbpaste | python
66.965788
real 0m1.063s
user 0m1.017s
sys 0m0.094s
突出して速いJIT御三家です.但しPyPyはこの中では少し遅め.Python2のほうがPython3より速いことも特徴的です.
mpyw@localhost:~$ time pbpaste | php
66.90856
real 0m1.028s
user 0m1.040s
sys 0m0.027s
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が載ればどうなるんでしょうか.
mpyw@localhost:~$ time pbpaste | ruby
67.006156
real 0m1.760s
user 0m1.728s
sys 0m0.081s
mpyw@localhost:~$ time pbpaste | php
67.087172
real 0m1.721s
user 0m1.744s
sys 0m0.028s
RubyやPHP5もPHP7に次いで速いです.
mpyw@localhost:~$ time pbpaste | perl
66.88662
real 0m2.612s
user 0m2.598s
sys 0m0.023s
mpyw@localhost:~$ time pbpaste | lua
66.94308
real 0m2.627s
user 0m2.603s
sys 0m0.022s
JITが無くてもそこそこ速いLua. 歴史あるPerlも並んでます.
mpyw@localhost:~$ time pbpaste | python
67.098124
real 0m3.859s
user 0m3.846s
sys 0m0.032s
mpyw@localhost:~$ time pbpaste | python
66.947476
real 0m4.444s
user 0m4.370s
sys 0m0.105s
JIT無しでもPython2のほうがPython3より速いことは共通してますね.
mpyw@localhost:~$ time pbpaste | RScript -
66.89728
real 1m0.795s
user 0m58.386s
sys 0m0.723s
Rはお察し
実行速度の関係
何をするかにも大きく左右されますが,あくまでズンドコキヨシるという前提で示します.JITを採用しているものは太字で示します.
-
Node.js
LuaJIT - PyPy (Python 2 JIT)
-
PyPy (Python 3 JIT)
PHP 7 - HHVM (PHP 5 JIT)
- PHP 5
Ruby - Perl
Lua - Python 2
- Python 3
---宝くじで1億当たるぐらいの確率でしか超えられない壁--- - R
こうなりました.PHPはメジャーバージョンアップで速くなったのになんでPythonは遅くなってんの…
番外編
VMの起動が計測時間に含まれていないので他のものと比較は出来ませんが,Webブラウザのコンソールでも計測してみました.すると,書き方によってかなりの差が現れました.
'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');
'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による最適化が入らないっぽいですね.