.NET 8 で標準のシャッフルが用意されたということで、OrderBy(_ => Guid.NewGuid())
との差を確認しておいてみる。
念のため触れておくと、OrderBy(_ => Guid.NewGuid())
は、本来ソートに使うメソッドに本来Guid
として生成する数値を渡し、シャッフルとして振る舞わせる手法。
・BenchmarkDotNet
・int[]
とIEnumerable<int>
でそれぞれ計測
・非破壊的に行う(Random.Shuffle
に渡す配列は元とは別に用意)
ベンチマークコード折りたたみ
private readonly int n = 256; private int[] array = default!; private IEnumerable<int> enumerable = default!; [GlobalSetup] public void Setup() { array = new int[n]; static IEnumerable<int> getSequence(int n) { for (int i = 0; i < n; i++) yield return 0; } enumerable = getSequence(n); } [Benchmark(Baseline = true)] [BenchmarkCategory("Array")] public int[] ShuffleArray() { var dest = new int[array.Length]; array.CopyTo(dest, 0); Random.Shared.Shuffle(dest); return dest; } [Benchmark] [BenchmarkCategory("Array")] public int[] GuidShuffleArray() => array.OrderBy(_ => Guid.NewGuid()).ToArray(); [Benchmark(Baseline = true)] [BenchmarkCategory("Enumerable")] public int[] ShuffleEnumerable() { var dest = enumerable.ToArray(); Random.Shared.Shuffle(dest); return dest; } [Benchmark] [BenchmarkCategory("Enumerable")] public int[] GuidShuffleEnumerable() => enumerable.OrderBy(_ => Guid.NewGuid()).ToArray();
Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | ---------------------- |----------:|-----------:|----------:|------:|--------:|-------:|----------:|------------:| ShuffleArray | 2.220 μs | 0.2647 μs | 0.0145 μs | 1.00 | 0.00 | 0.3319 | 1.02 KB | 1.00 | GuidShuffleArray | 46.852 μs | 28.3296 μs | 1.5528 μs | 21.10 | 0.74 | 2.3193 | 7.27 KB | 7.10 | | | | | | | | | | ShuffleEnumerable | 3.369 μs | 0.6131 μs | 0.0336 μs | 1.00 | 0.00 | 0.7477 | 2.3 KB | 1.00 | GuidShuffleEnumerable | 46.495 μs | 1.4682 μs | 0.0805 μs | 13.80 | 0.16 | 2.7466 | 8.43 KB | 3.67 |
21倍 / 14倍違う。けれど、長さ256で数十マイクロ秒程度の差しかないとも取られえそう。
OrderBy
シャッフルは標準でIEnumerable<T>
(そしてそれを実装するList<T>
) から、しかも fluent に書けるという大きな強みがあるので、今後も手法の一つとして扱われそう。