結論
IndexOfAnyExcept
を使えば速い
(追記:有り無しを調べるだけならContainsAnyExcept
があるみたいです)
動機
ReadOnlySpan<byte>
があったとして、その要素がすべて0xff
かどうかを判断したかった。
やってみたこと
foreach
を使う
一番シンプルなやり方はforeach
を使うやり方。
public bool ForeachLoop()
{
var span = data.AsSpan();
foreach (var byteData in span)
{
if (byteData != 0xff)
{
return false;
}
}
return true;
}
foreach
ではなくてfor
を使う
foreach
を使わずにfor
を使ってインデックスで参照した場合
public bool ForLoop()
{
var span = data.AsSpan();
for (var i = 0; i < span.Length; i++)
{
if (span[i] != 0xff)
{
return false;
}
}
return true;
}
IndexOfAnyExcept
を使う
この記事の本命。引数で与えられた数値以外のデータが見つかったらそのインデックスを返すという関数。見つからなかった場合は-1を返します。MemoryExtensions
の中にあるみたいです。他にも便利そうなやつが色々定義されてました。
public bool IndexOfAnyExcept()
{
var span = data.AsSpan();
return span.IndexOfAnyExcept((byte)0xff) == -1;
}
ベンチマーク
.NET 9でBenchmarkdotnetを使って計測してみました。32KBの大きさの全てが0xff
バイト配列を渡してます。
// * Summary *
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.3194)
Intel Core i5-14500, 1 CPU, 20 logical and 14 physical cores
.NET SDK 9.0.101
[Host] : .NET 9.0.0 (9.0.24.52809), X64 AOT AVX2
DefaultJob : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2
| Method | Mean | Error | StdDev |
|----------------- |-----------:|---------:|---------:|
| ForeachLoop | 9,071.5 ns | 93.23 ns | 82.64 ns |
| ForLoop | 9,069.3 ns | 82.11 ns | 76.81 ns |
| IndexOfAnyExcept | 391.2 ns | 7.70 ns | 7.57 ns |
// * Hints *
Outliers
Bench.ForeachLoop: Default -> 1 outlier was removed (9.29 us)
Bench.IndexOfAnyExcept: Default -> 1 outlier was removed (415.33 ns)
// * Legends *
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
1 ns : 1 Nanosecond (0.000000001 sec)
IndexOfAnyExcept
が圧倒的に速かったです。byte型でしか試していませんが、他の数値型でも速くなるのかな。