自分が普段利用している言語や、気になっている言語などを集めてベンチマークを行いました。
次の2つのブログ記事の伝統に則り、再帰のフィボナッチ関数を使って、処理時間や利用メモリなどを計測してみました。
- この頃 流行りの 言語たち(他)でベンチマーク (Dart, Go, Julia, Nim, Python, Rust 他) - Blank File
- フィボナッチで各種言語をベンチマーク - satosystemsの日記
ただし、値は埋め込みではなくて、コマンドライン引数として取れるように変更しています (定数で最適化されると嫌なので)。
言語紹介
測定したのは、次の20言語です。
- C (Clang)
- C++ (Clang)
- Chapel
- Crystal
- D (DMD, LDC)
- Elixir
- Felix
- Go
- Julia
- Lua (LuaJIT)
- Nim
- JavaScript (Node)
- OCaml
- PHP
- Perl
- Pony
- Python (Python, Nuitka, PyPy)
- Ruby
- Rust
- Swift
Javaを入れるのが嫌なので、Java VM系の言語は一切ありません。
ソースなどは次のリポジトリにあります。
というわけで、各言語 (環境) を紹介。TIOBE Index for October 2015のランク上位の言語から順に紹介していきます。
C
#include <stdio.h> #include <stdlib.h> int fib(int n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); } int main(int argc, char** argv) { printf("%d\n", fib(atoi(argv[1]))); return 0; }
他の言語もそうですが、コンパイルエラーにならない限りはエラー処理は極力無視するスタイルです。
コンパイルはclang
なので-Ofast
。
clang -Ofast -o fib-c fib.c
C++
19個だときりが悪いということで最後に急遽追加。
#include <iostream> #include <string> int fib(int n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); } int main(int argc, char** argv) { std::cout << fib(std::stoi(argv[1])) << std::endl; return 0; }
C++11からstoiが使えるのでちょっと楽になりました。
コンパイルはCのときと同じ-Ofast
。
clang++ -Ofast -o fib-c++ fib.cc
Python
スクリプト言語としては、一番よく利用して一番好きな言語です。 3項演算子の書きづらさだけはどうにかして欲しいですけれど。
Macに初期状態で入っているpython
をそのまま利用しています。
import sys def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2) print(fib(int(sys.argv[1])))
実行時のオプション指定はなし。
python ./fib.py
PyPy
PythonのJIT実行環境。最近4.0.0が出ました。
ソースはPythonのをそのまま利用。
実行時オプションがいろいろ曲者で、設定次第で速度が倍以上違うので困りました。そこそこ速そうな--jit function_threshold=5000
を利用しています。
$ time pypy ./fib.py 42 267914296 pypy ./fib.py 42 10.06s user 0.09s system 99% cpu 10.181 total $ time pypy --jit function_threshold=5000 ./fib.py 42 267914296 pypy --jit function_threshold=5000 ./fib.py 42 3.79s user 0.07s system 99% cpu 3.878 total
Nuitka
ネイティブバイナリを作ることができるPythonコンパイラ、ということでPythonと比べてどれくらい早くなるかを試してみました。 ソースはPythonのものをそのまま利用しています。
コンパイルは--clang
を付けましたが多分省略しても同じ。
nuitka --clang fib.py
PHP
ハイパーテキスト向けのプリプロセッサ。 もう少しでPHP 7もリリースされるんですかね。 Macに初期状態で入っていたので、書きました。
<?php function fib($n) { return $n < 2 ? $n : fib($n - 1) + fib($n - 2); } echo fib($argv[1]), "\n"; ?>
実行時のオプション指定はなし。
php ./fib.php
Node
Chrome V8を搭載したJavaScript実行環境。
function fib(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); } console.log(fib(process.argv[2]));
V8のオプションがいろいろあって死にそうになりましたが、ちょっといじってみても変わりがなかったので、オプションなしでデフォルトのまま実行しています。
node ./fib.js
Perl
言わずと知れたスクリプト言語。蝶のロゴが可愛いPerl 6はPerl 5の妹らしい。
Macに初期状態で入っているperl
をそのまま利用しています。
sub fib { my $n = shift; return $n < 2 ? $n : fib($n - 1) + fib($n - 2); } print fib($ARGV[0]), "\n";
実行時のオプション指定はなし。
perl ./fib.pl
ちなみに、Perl 6も試したのですが、Perl 5以上に猛烈に遅いので今回はベンチマーク対象にしていません (ソースはfib.pl6)。
$ time perl6 --optimize=3 fib.pl6 30 832040 perl6 --optimize=3 fib.pl6 30 13.19s user 0.32s system 99% cpu 13.539 total $ time perl fib.pl 30 832040 perl fib.pl 30 0.83s user 0.01s system 99% cpu 0.845 total
Ruby
スクリプト言語。
Macに初期状態で入っているruby
をそのまま利用しています。
def fib(n) if n < 2 n else fib(n - 1) + fib(n - 2) end end puts fib(ARGV[0].to_i())
実行時のオプション指定はなし。
ruby ./fib.rb
Crystal
Ruby作者のMatz氏も驚くRuby風のコンパイル言語。LLVM BC経由でネイティブバイナリを吐いてくれます。
これ、Rubyでもそのまま動くんじゃないか? Crystalすげーっ。 https://t.co/pO0F0vqTly
— Yukihiro Matsumoto (@yukihiro_matz) June 16, 2015
ソースはRubyのものをそのまま利用しています。
ちなみに、3項演算子あたりはRubyと違うようなので、両方で実行できるようなソースにしています。
コンパイル時オプションには--release
。さらに、リンクエラーになったので-L/usr/local/lib
を追加しています。
crystal build --link-flags -L/usr/local/lib --release -o fib-crystal fib.rb
Swift
年末にはオープンソースになる予定のApple製の言語。 TIOBEでのランクもじわじわと上がっています。というかObjective-Cがランク下げすぎです。
func fib(n: Int) -> Int { return n < 2 ? n : fib(n - 1) + fib(n - 2) } print(fib(Int(Process.arguments[1])!))
コンパイルオプションは-O
。
swiftc -O -o fib-swift fib.swift
D
Facebookが手を入れ始めた言語。
import std.stdio; import std.conv; int fib(int n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); } void main(string[] args) { writeln(fib(to!int(args[1]))); }
コンパイラはDMDとLDCの2種類あり、コンパイルオプションはそれぞれ次の通り。-O5
とか実装があるのを始めて見ました。
dmd -m64 -release -O -offib-d fib.d ldc2 -L=-w -O5 -m64 -offib-d-ldc fib.d
LuaJIT
軽量スクリプト言語として有名なLuaのJITコンパイラです。
function fib(n) if (n < 2) then return n else return fib(n - 1) + fib(n - 2) end end print(fib(tonumber(arg[1])))
実行時オプションは-O3
。
luajit -O3 ./fib.lua
Elixir
Erlang VM上で動作する関数型言語。 今回始めて書きましたが、モジュール宣言は必須なのかがいまいちわからず。
defmodule Fibonacci do def fib(n) when n < 2 do n end def fib(n) do fib(n - 1) + fib(n - 2) end end if System.argv != [] do IO.puts Fibonacci.fib(String.to_integer(List.first(System.argv()))) end
実行時オプションは特になし。
elixir ./fib.exs
OCaml
下で紹介しているFelixをコンパイルするための副産物として入ったので、ついでにコードも書いてみました。
今回始めて書いたので文法がわからずに苦労しました。
let rec fib n = if n < 2 then n else fib (n - 1) + fib (n - 2);; print_int (fib (int_of_string Sys.argv.(1))); print_newline ()
最適化コンパイラのocamlopt
を使うのでコンパイラオプションは特になし。
ocamlopt -o fib-ocaml fib.ml
Rust
Mozilla Research製の言語。if let Some
のあたりはSwiftっぽいですね。
use std::env; fn fib(n: isize) -> isize { if n < 2 { n } else { fib(n - 1) + fib(n - 2) } } fn main() { if let Some(s) = env::args().nth(1) { if let Ok(n) = s.parse::<isize>() { println!("{}", fib(n)); } } }
コンパイルオプションは-O
のみ。
rustc -O -o fib-rust fib.rs
Go
TIOBEでのランクだと50位より下だったことにちょっと驚いてしまったGoogle製の言語。 マスコットキャラのGopherが可愛い。
package main import ( "fmt" "os" "strconv" ) func fib(a int) int { if a < 2 { return a } else { return fib(a - 1) + fib(a - 2) } } func main() { n, _ := strconv.Atoi(os.Args[1]) fmt.Println(fib(n)) }
コンパイルオプションはないっぽい。build
にすると最適化されているのだろうか…。
go build -o fib-go fib.go
Julia
MathematicaやMATLABの流れを汲む科学計算向けの言語、らしい。
fib(n) = n < 2 ? n : fib(n - 1) + fib(n - 2) println(fib(parse(ARGS[1])))
実行時には最適化オプションの-O
を追加。
julia -O ./fib.jl
Nim
マルチパラダイム言語。昔はPascalで書かれていたらしい。確かにresult
に代入できたりするところがPascalっぽい。
デフォルトではCソースを吐いてからそれをコンパイルしています。
import os import strutils proc fib(n: int): int = result = if n < 2: n else: fib(n - 1) + fib(n - 2) echo(fib(parseInt(paramStr(1))))
コンパイルオプションはいろいろ付けていますが、-d:release
が重要。
nim c --verbosity:0 -d:release --app:console --opt:speed --out:fib-nim fib.nim
Chapel
ここからマイナー言語の紹介。といっても、これはGitHubのTrending repositoriesに名前があるだけ有名なほうです。
スーパーコンピュータで有名なクレイがつくった並列計算のための言語 (ソースはGitHubで公開)。 2004年くらいから開発が始まっているみたいですが、息が長い割にまったく名前を聞いたことがなかったです。
proc fib(n): int return if n < 2 then n else fib(n - 1) + fib(n - 2); config var n = 1; writeln(fib(n));
おもしろいことに、グローバル変数の宣言にconfig
と付けると、コマンドライン引数と見なしてくれる機能があること (上の例だと、--n=4
みたいに指定できるようになる)。
まさに、コマンドラインツールを書くための言語。
コンパイル引数はよくありがちな-O
。
chpl -O -o fib-chapel fib.chpl
Pony
Erlangのようにアクターモデルを持ちつつ、LLVMベースでネイティブバイナリにコンパイルできるため、実行パフォーマンスがよい言語。
actor Main fun fib(n: U32): U32 => if n < 2 then return n else return fib(n - 1) + fib(n - 2) end new create(env: Env) => let n: U32 = try env.args(1).u32() else 0 end env.out.print(fib(n).string())
コンパイル引数はちょっと特殊でponyc
のみでよく、カレントディレクトリのソースを捜索してコンパイルするみたいです。
ponyc
Felix
恐らく知っている人はいないであろう超マイナー言語。言語のロゴはこれでいいのか…? 5年ぶりくらいにメジャーアップデートが出そうな気配です。言語的にはC++のトランスレータ的な趣きが強い。 C++との連携にグルーコードが不要とか、Cよりも早いバイナリを作るよとか書かれていて気になったので試してみました。
最近、WindowsとLinuxにはバイナリが用意されるようになったみたいですが、 Macではバイナリがない上に2015.10.30-rc8だとコンパイルエラーになるので、どうにかコンパイルできたコミット2b4c4a7のバイナリを利用しています。
fun fib (n:int) => if n < 2 then n else fib(n - 1) + fib(n - 2) endif; println $ str $ fib $ int $ System::argv 1;
コンパイルオプションは次の通り。
flx --static -c -O3 -o fib-felix fib.flx
速度
上で紹介したベンチマーク記事が42を使っていたので、各言語でfib(42)した結果をグラフにまとめました。
コンパイル言語のみでまとめなおしたものがこちら。最速はOCamlで、続いてSwift, Rust, D (LDC), Felix, Crystal, Cとなりました。 OCamlとSwiftが一歩だけ抜けて早いかんじです。使っている分にはSwiftが早い感じはあまりしませんけれど。
fib(1)のときはあまり処理がないので、だいたい起動処理時間と思ってよいはず…ということでスクリプト言語の起動時間としてまとめたものがこちら。コンパイル言語はすべて起動時間0なので省略しています。 Perlの圧倒的な起動時間の速さに驚かされます。
で、フィボナッチの値を1から45 (スクリプト言語は42) まで変化させてみたときの結果は次の通り。 全てを覆い、天を突き抜けようとしているのがPerlです。 PyPyは処理が多くなると最適化をかけ直したりするのか、処理時間が安定しません。
上のグラフを、右下のみを拡大させた結果は次のようになります。
全体を対数グラフにしたものがこちら。Juliaの「ウサギとカメ」のカメ感がなかなかよいです。
右下のみを拡大して見やすくしたものがこちら。 こうして見ると、OCamlとSwiftの2つはグラフが被っていて、だいたい同じ結果になっていることがわかります。
実行時メモリ使用量
それぞれ、fib(35)
の値を取るときに/usr/bin/time -l
して、そのmaximum resident set sizeの値を結果としました。Juliaがメモリ食いすぎでした。
言語/環境 | サイズ (バイト) |
---|---|
C | 688,128 |
C++ | 692,224 |
Felix | 794,624 |
Nim | 819,200 |
OCaml | 864,256 |
Pony | 995,328 |
LuaJIT | 1,134,592 |
Rust | 1,134,592 |
D (DMD) | 1,142,784 |
Crystal | 1,236,992 |
D (LDC) | 1,327,104 |
Go | 1,462,272 |
Perl | 1,683,456 |
Chapel | 1,839,104 |
Swift | 3,629,056 |
Python | 4,759,552 |
Nuitka | 4,849,664 |
Ruby | 7,135,232 |
PHP | 9,342,976 |
Node | 18,325,504 |
Elixir | 33,153,024 |
PyPy | 51,376,128 |
Julia | 97,636,352 |
コンパイル時間
コンパイラ言語のみ、コンパイル時間を計測してみました。
直前にmake && make clean
してからの結果。
言語/環境 | 時間 (秒) |
---|---|
C | 0.06 |
OCaml | 0.12 |
Felix | 0.23 |
D (dmd) | 0.25 |
Rust | 0.26 |
Swift | 0.26 |
Go | 0.37 |
C++ | 0.42 |
D (ldc) | 0.64 |
Nim | 0.68 |
Nuitka | 1.57 |
Crystal | 2.06 |
Pony | 2.42 |
Chapel | 3.26 |
バイナリ
コンパイラ言語のバイナリの状況です。
まずはファイルサイズ。Cは群を抜いて小さいですね。
言語/環境 | サイズ (バイト) |
---|---|
C | 8,504 |
C++ | 10,180 |
Swift | 17,560 |
Nim | 42,896 |
Crystal | 65,580 |
Pony | 82,432 |
Nuitka | 88,200 |
OCaml | 187,596 |
Rust | 277,604 |
Felix | 497,088 |
Chapel | 520,132 |
D (dmd) | 594,608 |
D (ldc) | 2,160,220 |
Go | 2,324,112 |
続いて、依存ライブラリ。 Goは何にも依存していません。これならバイナリサイズが大きいのも納得です。 libSystem.B.dylibは他の環境で言うところのlibcに当たるものらしいので、C言語やC言語に変換系の言語ではこれに依存しています。
fib-go: fib-c: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-d: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-d-ldc: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-nim: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-rust: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-ocaml: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-pony: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-c++: /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-felix: /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-swift: /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) @rpath/libswiftCore.dylib (compatibility version 0.0.0, current version 0.0.0) fib-nuitka: /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0) /System/Library/Frameworks/Python.framework/Versions/2.7/Python (compatibility version 2.7.0, current version 2.7.10) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) fib-chapel: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1) /usr/local/opt/chapel/libexec/third-party/gmp/install/darwin-clang-native/lib/libgmp.10.dylib (compatibility version 13.0.0, current version 13.0.0) /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 120.1.0) fib-crystal: /usr/local/opt/libevent/lib/libevent-2.0.5.dylib (compatibility version 7.0.0, current version 7.9.0) /usr/local/opt/libpcl/lib/libpcl.1.dylib (compatibility version 2.0.0, current version 2.11.0) /usr/lib/libpcre.0.dylib (compatibility version 1.0.0, current version 1.1.0) /usr/local/opt/bdw-gc/lib/libgc.1.dylib (compatibility version 2.0.0, current version 2.3.0) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)
どれも問題なく64-bitバイナリでした。
fib-c: Mach-O 64-bit executable x86_64 fib-c++: Mach-O 64-bit executable x86_64 fib-chapel: Mach-O 64-bit executable x86_64 fib-d: Mach-O 64-bit executable x86_64 fib-d-ldc: Mach-O 64-bit executable x86_64 fib-crystal: Mach-O 64-bit executable x86_64 fib-go: Mach-O 64-bit executable x86_64 fib-felix: Mach-O 64-bit executable x86_64 fib-nim: Mach-O 64-bit executable x86_64 fib-rust: Mach-O 64-bit executable x86_64 fib-swift: Mach-O 64-bit executable x86_64 fib-ocaml: Mach-O 64-bit executable x86_64 fib-nuitka: Mach-O 64-bit executable x86_64 fib-pony: Mach-O 64-bit executable x86_64
雑感
- 最速はOCamlとSwift
- Goのシングルバイナリ感がすごい
- Perlは実行速度は遅いが、起動の軽さとメモリ利用量の少なさがよい
- PythonはRubyより遅いのね…
- Rustは1.4.0になって、Cと同等の速度になった (1.3.0はCの4割くらい遅かった)
- CrystalはだいたいC同じくらいの速度。ただしコンパイルは遅い
- Juliaは起動が遅いが、それ以降はコンパイラ言語並みに早い
- PyPyの速度の不安定さ。ちゃんと使うならチューニング必須
- Luaのいろいろな軽さ
バージョン
基本的に、brew, brew cask, pipを利用しております。
- Rust: 最新版パッケージのなかったので公式サイトよりパッケージインストール
- Nim: 最新版パッケージのなかったので公式サイトよりビルド
- Felix: パッケージのないのでソースよりビルド (コミット2b4c4a7)
言語/環境 | バージョン |
---|---|
C, C++ (Clang) | Apple LLVM version 7.0.0 (clang–700.1.76) |
Chapel | chpl Version 1.12.0 |
Crystal | Crystal 0.9.1 (Fri Oct 30 13:49:50 UTC 2015) |
D (dmd) | DMD64 D Compiler v2.068 |
D (ldc) | LDC - the LLVM D compiler (0.16.0): |
Elixir | Elixir 1.1.1 |
Felix | version 15.08.15 |
Go | go version go1.5.1 darwin/amd64 |
Julia | julia version 0.4.0 |
LuaJIT | LuaJIT 2.0.4 – Copyright (C) 2005–2015 Mike Pall. http://luajit.org/ |
Nim | Nim Compiler Version 0.12.0 (2015–10–27) [MacOSX: amd64] |
Node | v5.0.0 |
Nuitka | 0.5.15 |
OCaml | The OCaml toplevel, version 4.02.3 |
PHP | PHP 5.5.27 (cli) (built: Aug 22 2015 18:31:33) |
Perl | This is perl 5, version 18, subversion 2 (v5.18.2) built for darwin-thread-multi–2level |
Pony | 0.2.1 |
Python | Python 2.7.10 |
PyPy | [PyPy 4.0.0 with GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang–700.1.76)] |
Ruby | ruby 2.0.0p645 (2015–04–13 revision 50299) [universal.x86_64-darwin15] |
Rust | rustc 1.4.0 (8ab8581f6 2015–10–27) |
Swift | Apple Swift version 2.1 (swiftlang–700.1.101.6 clang–700.1.76) |
測定環境
- MacBook Pro (Retina, Mid 2012)
- Processor 2.6 GHz Intel Core i7
- Memory 16 GB 1600 MHz DDR3
- OS OS X El Capitan (15A279b)
0 件のコメント:
コメントを投稿
注: コメントを投稿できるのは、このブログのメンバーだけです。