はじめに
今回はSpan<T>
とString.Substring
でどれくらい速度差が出てくるのか調べてみる記事になります。
https://docs.microsoft.com/ja-jp/dotnet/api/system.span-1?view=net-6.0
String.Substring メソッド (System) | Microsoft Docs
一応公式ドキュメントにSpan<T>
とSlice
という項目があり、以下のように書かれています。
Span
には、現在のスパンより、指定したインデックスから始まるスライスを形成するSliceメソッドの 2 つのオーバー ロードが含まれています。 これにより、Span のデータを、パフォーマンスの影響を最小限にしながら、データ処理パイプラインの一部で必要に応じて処理できる、一連の論理的なまとまりとして扱うことができます。 たとえば、最新のサーバー プロトコルは多くの場合、テキスト ベースであるため、文字列と部分文字列の操作は特に重要です。 Stringクラスの場合、部分文字列の抽出に使う主要なメソッドはSubstringです。 広範な文字列操作に依存するデータ パイプラインは、次のように、いくつかのパフォーマンスの低下を招きます。
1. 部分文字列を保持する新しい文字列を作成します。
2. 元の文字列から新しい文字列に文字のサブセットをコピーします。
https://docs.microsoft.com/ja-jp/dotnet/api/system.span-1?view=net-6.0#spant-and-slices
まあそうなのでしょうが、一応疑り深いので手元でも試してみました。
環境
.NET Core3.1
Visual Studio v17.0.5
MacOS
実験
// 適当に長そうな文字列を作る string sample = Enumerable.Range(0, 100) .Select(_ => Guid.NewGuid().ToString()) .Aggregate((x, y) => x + y); // 3600文字 Console.WriteLine(sample); Console.WriteLine($"Length : {sample.Length}"); const int count = 10000000; const int startIndex = 1000; const int length = 1000; var sw = new Stopwatch(); // String.Substring sw.Start(); for (var i = 0; i < count; i++) { string _ = sample.Substring(startIndex, length); } sw.Stop(); Console.WriteLine($"string.Substring : {sw.ElapsedMilliseconds}ms"); // Span<T> sw.Reset(); sw.Start(); for (var i = 0; i < count; i++) { ReadOnlySpan<char> _ = sample.AsSpan().Slice(startIndex, length); } sw.Stop(); Console.WriteLine($"Span<T> : {sw.ElapsedMilliseconds}ms");
結果
種類 | 処理時間 |
---|---|
string.Substring |
1395ms |
Span<T> |
153ms |
上記のコードを実行してみたところ、結構速度に違いが出てきているようです。
補足
実験で用いたコードの返り値が異なることに注意してください。
string _ = sample.Substring(startIndex, length); ReadOnlySpan<char> _ = sample.AsSpan().Slice(startIndex, length);
具体的に何が違うのかといったものは未確認飛行さんの部分参照の箇所を参照すると良いと思います。
Span<T>構造体 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
補足2
また少し面白い実験としてReadOnlySpan
をstring
に変換したら、割と同じ処理時間になります。(少しオーバーヘッドがあるが)
ReadOnlySpan<char> a = sample.AsSpan().Slice(startIndex, length); _ = new string(a);
種類 | 処理時間 |
---|---|
string.Substring |
1340ms |
Span<T> |
1554ms |