Home / ぼやきごと / 2020-01-18
2020-01-18

C#:続! ReadOnlySpan と Range を駆使して高速文字列分割

Prev: [C#: ReadOnlySpan と Range を駆使して高速文字列分割]

前回、 ReadOnlySpan<T>Range を駆使して高速に文字列分割*1する拡張メソッドを作り、そのベンチマークを行いました。

…が、「ベンチマークに BenchmarkDotNet を使わないとか舐めてるの?C#erの面汚しめ!」という心の声に悩まされるようになったので全面的に書き換えました。

GitHub: ruche7/Test_SplitReadOnlySpan at boyaki_200118 (以前のコードは boyaki_200110 タグにあります)

  • BenchmarkDotNet を使うようにした。
  • 余計な処理はせず、 string を改行で区切って string[] にするまでの処理に絞って計測するようにした。
    • セパレータは '\n' オンリーと "\r\n", "\n", "\r" 混合の2パターン。
  • SpanExtensions 静的クラス(ReadOnlySpanExtensions から名前変更)に拡張メソッドを追加。
    • Span<T> に対応。
    • ReadOnlySpan<char>Span<char> 限定で複数セパレータ(string[])による分割に対応。(string.Split では元々可能)

事前に下記の静的フィールドを準備しておきます。

// 改行を表す文字列の配列
private static readonly string[] LineBreaks = { "\r\n", "\n", "\r" };

// LineN = N行の文字列
// 改行コードは "\r\n", "\n", "\r" 混在
private static readonly string Line1 = MakeString(1);
private static readonly string Line10 = MakeString(10);
private static readonly string Line100 = MakeString(100);
private static readonly string Line1000 = MakeString(1000);
private static readonly string Line10000 = MakeString(10000);

計測対象メソッドは下記の4パターン。 N は上記フィールドの通り 1, 10, 100, 1000, 10000 の5通りで、総メソッド数は20個。

String_SplitNewLine_LineN
  • 文字列 LineNstring.Split('\n') で分割する。
Span_SplitNewLine_LineN
  1. 文字列 LineNstring.AsSpan()ReadOnlySpan<char> に変換する。
  2. ReadOnlySpan<char>.SplitToRanges('\n') で各分割文字列範囲を表す List<Range> を得る。
  3. new string[ranges.Count] で作成した文字列配列に各行を span[ranges[i]].ToString() で設定。
String_SplitLineBreaks_LineN
  • 文字列 LineNstring.Split(LineBreaks, StringSplitOptions.None) で分割する。
Span_SplitLineBreaks_LineN
  1. 文字列 LineNstring.AsSpan()ReadOnlySpan<char> に変換する。
  2. ReadOnlySpan<char>.SplitToRanges(LineBreaks) で各分割文字列範囲を表す List<Range> を得る。
  3. new string[ranges.Count] で作成した文字列配列に各行を span[ranges[i]].ToString() で設定。

要するに、 Xxx_SplitNewLine_LineN が単一セパレータ(char)による分割、 Xxx_SplitLineBreaks_LineN が複数セパレータ(string[])による分割です。

ベンチマークの結果は README.md に載せてありますMean の列を見ればOK。

1行を単一セパレータで分割するケースを除き ReadOnlySpan<char>.SplitToRanges を使う方が高速で、特に複数セパレータの場合は2〜5倍もの速度差になっています。

自作のツール等では積極的に利用していきたいですね。

Category: [C#][Visual Studio][プログラミング] - 2020-01-18 06:03:49

*1 というか文字列に限らず ReadOnlySpan<T> なら何でも分割