はじめに
TwitterでD言語の日本語情報増やしたいなーとか言っていたところ、タイミングよく文字列操作で良い記事があったのでちょっと読んでいました。
- 文字列を改行で確実に分割する
Pythonも速いんだなーとか思いつつ、この言語の並びならD言語が1番じゃない…?と思ったので、ベンチマーク部分について軽く追試しつつ盛れるだけ盛ってやろうと思った次第です。(盛ってはいけない)
ちなみに比較対象は、元から結果が上位だった Python(pypy3) と JavaScript(Node) に絞りますのでご了承ください
(たまたま手元で計測が容易だったものともいう)
前置き
計測にあたり元のソースコードは何も変更していませんのでベースはそちら参照してください。
https://github.com/yosgspec/splits
pypy3やnodeの引数は詳しくないので最適化っぽいものは何もやっていません。
というか言語のタイプが違いすぎるのであまり意味のある比較ではないという認識はあります。
それでもそれなりに肉薄してるので、これはそういうコンテンツなのだと思っていただければ幸いです。
何度でも書くけどネタ記事だよ!
計測
諸々書いてしまうと盛れないので先に結果発表です。
結果
- 傾向だけ見れればいいかということで、元のコードをローカル環境でそれぞれ10回計測しました
- 元々各言語で3種類の結果がありましたが、そのなかで一番速かったパターンを採用しています
- D言語はdmdとldc2という2つのコンパイラで実行しています
結果を表にまとめると以下の通りでした。
単位はミリ秒(msecs)で、????というのが盛ったやつです。詳細は後述します。
結果データ
# | dmd | ldc2 | pypy3 | Node | dmd + ???? | ldc2 + ???? |
---|---|---|---|---|---|---|
1 | 335 | 174 | 198 | 272 | 78 | 0 |
2 | 297 | 166 | 195 | 280 | 87 | 0 |
3 | 301 | 184 | 194 | 274 | 81 | 0 |
4 | 298 | 198 | 199 | 273 | 88 | 0 |
5 | 297 | 173 | 192 | 279 | 79 | 0 |
6 | 298 | 174 | 198 | 275 | 79 | 0 |
7 | 302 | 176 | 201 | 275 | 80 | 0 |
8 | 297 | 171 | 195 | 286 | 78 | 0 |
9 | 315 | 179 | 193 | 282 | 84 | 0 |
10 | 295 | 178 | 198 | 287 | 88 | 0 |
集計値
dmd | ldc2 | pypy3 | Node | dmd + ???? | ldc2 + ???? | |
---|---|---|---|---|---|---|
平均 | 303.5 | 177.3 | 196.3 | 278.3 | 82.2 | 0 |
中央値 | 298 | 175 | 196.5 | 277 | 80.5 | 0 |
最大値 | 335 | 198 | 201 | 287 | 78 | 0 |
最小値 | 295 | 166 | 192 | 272 | 88 | 0 |
標準偏差 | 12.44 | 6.60 | 2.91 | 5.38 | 4.16 | 0 |
盛らなくてもldc2が速かった!
そして盛ったdmdは速かったpypy3より倍以上速い!ldc2に至っては無限倍速い!すごい!!!
(素のdmd…お前…負けたんか…)
補足
- pypy3では
splitlines
よりreplace
とsplit
の組み合わせの方が速かったのでそちらを採用しました- 単純な
splitelines
のほうは平均で約370msecsでした。元記事より遅いのでなんか変ですね?そういうもの?
- 単純な
計測方法
環境
使ったのは Surface Book2 の Windows10 環境です。
CPUは、 第 8 世代 インテル® Core™ i7-8650U クアッド コア プロセッサ、4.2GHz Max Turbo
スペック:https://www.microsoft.com/ja-jp/p/surface-book-2/8mcpzjjcc98c?activetab=pivot:techspecstab
D言語
主要なコンパイラが3つありますが、今回は公式コンパイラのdmdとLLVMベースのldc2の2つを使います。
語弊がありますが、dmdはビルドが速い公式コンパイラ、ldc2はLLVMベースの実行が速いコンパイラです。
ちなみにどちらも2019年7月6日時点でそれぞれ最新版です。
dmd
dmd --version
DMD32 D Compiler v2.087.0
Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved written by Walter Bright
dmd splits_d.d -O -release -inline -boundscheck=off
app.exe
LDC
ldc2 --version
LDC - the LLVM D compiler (1.16.0):
based on DMD v2.086.1 and LLVM 8.0.0
built with LDC - the LLVM D compiler (1.16.0)
Default target: x86_64-pc-windows-msvc
Host CPU: skylake
http://dlang.org - http://wiki.dlang.org/LDC
ldc2 splits_d.d -O3 -release -boundscheck=off
app.exe
Node
2019/07/06時点のLTS版 v10.16.0 です。最新版は試していません。
node --version
v10.16.0
node splits.js
Python(pypy3)
Windows向けのpypy3を拾ってきて使いました。
pypy3 --version
Python 3.6.1 (784b254d6699, Apr 16 2019, 12:10:48)
[PyPy 7.1.1-beta0 with MSC v.1910 32 bit]
pypy3 splits.py
ベンチマークを盛る
盛るっていうよりなんだこれ?という結果ですが、何をしたかといえばD言語の特徴である CTFE
(コンパイル時関数評価)です。
D言語の関数には通称 CTFEable
と呼ばれる分類があり、一定の条件を満たすとコンパイル時に計算を実行でき、結果を定数として保持できます。
C++でも constexpr
がありますし、ぼちぼちメジャーな機能ではあります。
CTFEable
な任意の式は定数として保持できるので、この手のベンチマークならちょっと書き換えるだけで実行時間はほぼゼロになるだろう、という魂胆です。
(というかdmdはなんでゼロにならないんでしょうかね…?)
やったこと
というわけで書き換えたのは2行です。変数宣言と計算のところをenumにするだけ。
auto words = "asdff\nastgrw3h\r\nwtegole\rkserlhge3t\nearsgh\nergh\rsagr\r\nerghe\r";
// 中略
wordList = words.splitLines();
enum words = "asdff\nastgrw3h\r\nwtegole\rkserlhge3t\nearsgh\nergh\rsagr\r\nerghe\r";
// 中略
enum temp = words.splitLines();
wordList = temp;
まとめ
- 盛らなくてもD言語が1番速かった(よかった…)
- 改行で分割するのは
std.string
のsplitLines
で安定。 - ベンチマークを書くときは変に気を回す必要なし
- enumやグローバル変数の初期化式を書かない限りCTFEされない(条件は公式ドキュメントに書いてありそう)
- コンパイル時に評価することを期待する場合、
enum
で1回変数に落とすのが確実
- 改行で分割するのは
- pypy3はやはり速い。JITは正義。
- NodeのRegexは正規表現の中では異様に速い…裏で何かやってそう…
- pypy3もNodeも処理単体で見れば普通にdmdより速い(まじか)
あとがき
ぶっちゃけD言語のCTFEは「任意の式がCTFEできるかどうかチェックして、できるならやる」という仕組みだと思っていました。
なので最初にソース見たときの感想は「なんでこれで実行時間測れてるの?」です。
今回の結果から、ちゃんとCTFEするにはenumに1回入れる必要があることがわかりました。
「ベンチマークしたいときは普通に書いておけば大丈夫」ということですが、逆に言えば「何かの式で一部がCTFEされることはない」ので「定数っぽいところは1回enum挟んだほうが無難」ということになりそうです。(要追試)
ローカルのプログラムをいくつか見てみたところ、長い式を普通に書いてるところが結構見つかったので、要所にenum挟んでやればまだまだ速くできる余地がありそうです。
というわけでいろいろがんばっていきます!