.NET 9中ToList 和 ToArray 之间的性能比较

3a29977dbf5ad319afc3e14083679037.png

23d1efafed65c0be8479619c4b998309.png

今年,我写了一篇文章,比较了创建不会变异的短期集合时的性能,通常用于在迭代临时 LINQ 转换时防止多次枚举,或者确保在相应的应用程序层内抛出映射异常。

这些测试是使用 .NET Framework 4.8、.NET 7 和 .NET 8 执行的,得出的结论是,对于几乎所有集合大小,它们都明显更快,内存效率更高,唯一的例外是 .NET 8 中非常大的集合速度更快,但仍然使用更多内存。

假设一切按计划进行,Microsoft 应该在 2024 年底之前发布 .NET 9。这是他们最受欢迎的开发框架的下一个主要版本,它将带来许多新功能(C# 13 就是其中之一)和性能改进。

由于我们已经有可用的 .NET 9 预览版 5,其中包含内部使用的更优化的 ,我认为现在是比较这两种方法在 .NET 9 中的性能的好时机,同时使用 .NET 8 作为基线。

性能测试

再一次,我将使用众所周知的 C# 库来运行测试,环境如下所示:BenchmarkDotNet

BenchmarkDotNet v0.13.10, Windows 11 (10.0.22631.3737/23H2/2023Update/SunValley3)
AMD Ryzen 7 3700X, 1 CPU, 16 logical and 8 physical cores
.NET SDK 9.0.100-preview.5.24307.3
  [Host]               : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
  .NET 8.0             : .NET 8.0.6 (8.0.624.26715), X64 RyuJIT AVX2
  .NET 9.0             : .NET 9.0.0 (9.0.24.30607), X64 RyuJIT AVX2
  .NET Framework 4.8.1 : .NET Framework 4.8.1 (4.8.9241.0), X64 RyuJIT VectorSize=256

测试包括创建一个集合,该集合将保存大小由测试参数定义的随机整数。为确保集合初始化不会影响性能,将在测试设置期间创建并缓存集合初始化,但在调用 或 之前将其转换为新初始化。

请记住,我们想要测试遍历 an 的性能并创建一个数组或列表,因此,为了防止 .NET 内部优化(如使用 ),将缓存数组转换为 an 的方法将使用关键字。这与上一篇文章不同,因为我使用的是返回数组优化可枚举的方法,我想测试最坏的情况。

[SimpleJob(RuntimeMoniker.Net80, baseline: true)]
[SimpleJob(RuntimeMoniker.Net90)]
[MemoryDiagnoser]
public class ToListVsToArray
{
    [Params(10, 100, 1000, 10000, 100000)]
    public int Size;

    private int[] _items;

    [GlobalSetup]
    public void Setup()
    {
        var random = new Random(123);

        _items = Enumerable.Range(0, Size).Select(_ => random.Next()).ToArray();
    }

    [Benchmark]
    public int[] ToArray() => CreateItemsEnumerable().ToArray();

    [Benchmark]
    public List<int> ToList() => CreateItemsEnumerable().ToList();

    private IEnumerable<int> CreateItemsEnumerable()
    {
        foreach (var item in _items)
            yield return item;
    }
}

性能结果

由于这篇文章是我上一篇文章的延续,如果我得出结论,使用比 更快、更省内存,让我们比较一下该陈述是否仍然成立。ToArrayToList

| Method  | Size   | Mean          | Error         | StdDev        | Gen0     | Gen1     | Gen2     | Allocated |  
|-------- |------- |--------------:|--------------:|--------------:|---------:|---------:|---------:|----------:|  
| ToArray | 10     |      70.39 ns |      0.366 ns |      0.342 ns |   0.0134 |        - |        - |     112 B |  
| ToList  | 10     |      72.85 ns |      0.744 ns |      0.696 ns |   0.0315 |        - |        - |     264 B |  
| ToArray | 100    |     322.65 ns |      1.816 ns |      1.610 ns |   0.0563 |        - |        - |     472 B |  
| ToList  | 100    |     368.11 ns |      4.283 ns |      4.006 ns |   0.1469 |        - |        - |    1232 B |  
| ToArray | 1000   |   2,451.62 ns |     19.687 ns |     16.439 ns |   0.4845 |        - |        - |    4072 B |  
| ToList  | 1000   |   2,854.28 ns |     24.286 ns |     22.717 ns |   1.0109 |   0.0153 |        - |    8472 B |  
| ToArray | 10000  |  22,275.27 ns |    163.363 ns |    152.810 ns |   4.7607 |        - |        - |   40072 B |  
| ToList  | 10000  |  26,944.65 ns |    293.685 ns |    260.344 ns |  15.6250 |        - |        - |  131448 B |  
| ToArray | 100000 | 328,160.90 ns |  1,874.673 ns |  1,753.570 ns | 124.5117 | 124.5117 | 124.5117 |  400156 B |  
| ToList  | 100000 | 410,583.73 ns |  2,298.854 ns |  2,037.874 ns | 285.6445 | 285.6445 | 285.6445 | 1049120 B |

以基线为基准,我们可以看到该方法平均速度提高了 15%,使用的内存减少了 60%。ToListToArray

请记住,这甚至比较大的集合更好,这在 .NET 8 中并非如此,尽管使用较少的内存,但它的速度会慢 4%。ToArrayToList

获胜者:.NET 9.0

.NET 性能演变

由于我们还想比较 .NET 9 与 .NET 8 的性能,因此让我们在每个框架上单独分析每种方法,看看是否有任何更改。

ToArray

| Runtime  | Size   | Mean          | Error         | StdDev        | Ratio | RatioSD | Gen0     | Gen1     | Gen2     | Allocated | Alloc Ratio |  
|----------|------- |--------------:|--------------:|--------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|  
| .NET 8.0 | 10     |     107.51 ns |      1.585 ns |      1.238 ns |  1.00 |    0.00 |   0.0315 |        - |        - |     264 B |        1.00 |  
| .NET 9.0 | 10     |      70.39 ns |      0.366 ns |      0.342 ns |  0.65 |    0.01 |   0.0134 |        - |        - |     112 B |        0.42 |  
| .NET 8.0 | 100    |     442.33 ns |      3.788 ns |      3.543 ns |  1.00 |    0.00 |   0.1431 |        - |        - |    1200 B |        1.00 |  
| .NET 9.0 | 100    |     322.65 ns |      1.816 ns |      1.610 ns |  0.73 |    0.01 |   0.0563 |        - |        - |     472 B |        0.39 |  
| .NET 8.0 | 1000   |   3,186.13 ns |     31.530 ns |     29.493 ns |  1.00 |    0.00 |   1.0185 |        - |        - |    8544 B |        1.00 |  
| .NET 9.0 | 1000   |   2,451.62 ns |     19.687 ns |     16.439 ns |  0.77 |    0.00 |   0.4845 |        - |        - |    4072 B |        0.48 |  
| .NET 8.0 | 10000  |  30,659.83 ns |    292.167 ns |    273.293 ns |  1.00 |    0.00 |  12.6343 |        - |        - |  106232 B |        1.00 |  
| .NET 9.0 | 10000  |  22,275.27 ns |    163.363 ns |    152.810 ns |  0.73 |    0.00 |   4.7607 |        - |        - |   40072 B |        0.38 |  
| .NET 8.0 | 100000 | 482,397.96 ns |  1,499.949 ns |  1,403.053 ns |  1.00 |    0.00 | 249.5117 | 249.5117 | 249.5117 |  925140 B |        1.00 |  
| .NET 9.0 | 100000 | 328,160.90 ns |  1,874.673 ns |  1,753.570 ns |  0.68 |    0.00 | 124.5117 | 124.5117 | 124.5117 |  400156 B |        0.43 |

使用 .NET 8 作为基线,我们可以看到该方法在 .NET 9 上平均速度提高了 30%,使用的内存减少了 55%。ToArray

获胜者:.NET 9.0

待办事项

| Runtime  | Size   | Mean          | Error         | StdDev        | Ratio | RatioSD | Gen0     | Gen1     | Gen2     | Allocated | Alloc Ratio |  
|----------|------- |--------------:|--------------:|--------------:|------:|--------:|---------:|---------:|---------:|----------:|------------:|  
| .NET 8.0 | 10     |      87.44 ns |      1.803 ns |      1.929 ns |  1.00 |    0.00 |   0.0315 |        - |        - |     264 B |        1.00 |  
| .NET 9.0 | 10     |      72.85 ns |      0.744 ns |      0.696 ns |  0.84 |    0.02 |   0.0315 |        - |        - |     264 B |        1.00 |  
| .NET 8.0 | 100    |     420.90 ns |      3.654 ns |      2.853 ns |  1.00 |    0.00 |   0.1469 |        - |        - |    1232 B |        1.00 |  
| .NET 9.0 | 100    |     368.11 ns |      4.283 ns |      4.006 ns |  0.87 |    0.01 |   0.1469 |        - |        - |    1232 B |        1.00 |  
| .NET 8.0 | 1000   |   3,448.37 ns |     67.905 ns |     78.199 ns |  1.00 |    0.00 |   1.0109 |   0.0153 |        - |    8472 B |        1.00 |  
| .NET 9.0 | 1000   |   2,854.28 ns |     24.286 ns |     22.717 ns |  0.82 |    0.01 |   1.0109 |   0.0153 |        - |    8472 B |        1.00 |  
| .NET 8.0 | 10000  |  35,650.35 ns |    707.370 ns |  1,537.764 ns |  1.00 |    0.00 |  15.6250 |        - |        - |  131448 B |        1.00 |  
| .NET 9.0 | 10000  |  26,944.65 ns |    293.685 ns |    260.344 ns |  0.77 |    0.05 |  15.6250 |        - |        - |  131448 B |        1.00 |  
| .NET 8.0 | 100000 | 462,317.72 ns |  1,686.365 ns |  1,577.427 ns |  1.00 |    0.00 | 285.6445 | 285.6445 | 285.6445 | 1049120 B |        1.00 |  
| .NET 9.0 | 100000 | 410,583.73 ns |  2,298.854 ns |  2,037.874 ns |  0.89 |    0.01 | 285.6445 | 285.6445 | 285.6445 | 1049120 B |        1.00 |

使用 .NET 8 作为基线,我们可以看到该方法平均快 15%,同时在 .NET 9 上具有完全相同的内存占用。ToList

获胜者:.NET 9.0

结论

在本文中,我们比较了 .NET 9 与 .NET 9 上的性能,并再次得出结论,如果需要在内存中创建一个临时集合以防止多个枚举 ,则使用在所有情况下的性能都更高,而与集合大小无关,这在 .NET 8 中并非如此。

如果你喜欢我的文章,请给我一个赞!谢谢

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值