関連する質問 に関するKonrad Rudolph からのコメントに促されて、F# での正規表現のパフォーマンスをベンチマークする次のプログラムを作成しました。
open System.Text.RegularExpressions
let str = System.IO.File.ReadAllText "C:\\Users\\Jon\\Documents\\pg10.txt"
let re = System.IO.File.ReadAllText "C:\\Users\\Jon\\Documents\\re.txt"
for _ in 1..3 do
let timer = System.Diagnostics.Stopwatch.StartNew()
let re = Regex(re, RegexOptions.Compiled)
let res = Array.Parallel.init 4 (fun _ -> re.Split str |> Seq.sumBy (fun m -> m.Length))
printfn "%A %fs" res timer.Elapsed.TotalSeconds
および C++ での同等物:
#include "stdafx.h"
#include <windows.h>
#include <regex>
#include <vector>
#include <string>
#include <fstream>
#include <cstdio>
#include <codecvt>
using namespace std;
wstring load(wstring filename) {
const locale empty_locale = locale::empty();
typedef codecvt_utf8<wchar_t> converter_type;
const converter_type* converter = new converter_type;
const locale utf8_locale = locale(empty_locale, converter);
wifstream in(filename);
wstring contents;
if (in)
{
in.seekg(0, ios::end);
contents.resize(in.tellg());
in.seekg(0, ios::beg);
in.read(&contents[0], contents.size());
in.close();
}
return(contents);
}
int count(const wstring &re, const wstring &s){
static const wregex rsplit(re);
auto rit = wsregex_token_iterator(s.begin(), s.end(), rsplit, -1);
auto rend = wsregex_token_iterator();
int count=0;
for (auto it=rit; it!=rend; ++it)
count += it->length();
return count;
}
int _tmain(int argc, _TCHAR* argv[])
{
wstring str = load(L"pg10.txt");
wstring re = load(L"re.txt");
__int64 freq, tStart, tStop;
unsigned long TimeDiff;
QueryPerformanceFrequency((LARGE_INTEGER *)&freq);
QueryPerformanceCounter((LARGE_INTEGER *)&tStart);
vector<int> res(4);
#pragma omp parallel num_threads(4)
for(auto i=0; i<res.size(); ++i)
res[i] = count(re, str);
QueryPerformanceCounter((LARGE_INTEGER *)&tStop);
TimeDiff = (unsigned long)(((tStop - tStart) * 1000000) / freq);
printf("(%d, %d, %d, %d) %fs\n", res[0], res[1], res[2], res[3], TimeDiff/1e6);
return 0;
}
どちらのプログラムも、2 つのファイルを Unicode 文字列としてロードし (私は聖書のコピーを使用しています)、重要な Unicode 正規表現を構築し、\w?\w?\w?\w?\w?\w
分割された文字列の長さの合計を返す正規表現を使用して文字列を 4 回並列に分割します (割り当てを避けるために)。
64 ビットをターゲットとするリリース ビルドで Visual Studio (C++ に対して MP と OpenMP を有効にして) で両方を実行すると、C++ は 43.5 秒、F# は 3.28 秒かかります (13 倍以上高速)。.NET JIT は正規表現をネイティブ コードにコンパイルするのに対し、C++ stdlib はそれを解釈すると信じているため、これは私を驚かせませんが、ピア レビューが必要です。
私の C++ コードにパフォーマンス バグがありますか、それともコンパイルされた正規表現と解釈された正規表現の結果ですか?
EDIT : Billy ONeal は、.NET の解釈が異なる可能性があることを指摘した\w
ため、新しい正規表現で明示的にしました。
[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]?[0-9A-Za-z_]
これにより、実際に .NET コードが大幅に高速になり (C++ も同じ)、F# の時間が 3.28 秒から 2.38 秒に短縮されます (17 倍以上高速)。