てくメモ

trivial な notes

【C#】OrderBy(_ => Guid.NewGuid())

.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 に書けるという大きな強みがあるので、今後も手法の一つとして扱われそう。