.NET 9性能革命:Any()已非昔日吴下阿蒙,选型策略全面更新

作者:微信公众号:【架构师老卢】
7-2 8:9
7

历史回顾

还记得我们当初在代码审查中从Exists()转向Any(),因为"LINQ是未来"吗?然后花了数年时间争论微观优化?让我们聊聊为什么在.NET 9中这个争论变得简单多了。

旧规则(.NET 9之前)

在.NET 6-8时代,性能层级非常清晰:

Exists(): List的速度之王 在1万个元素时比Any()快3-4倍 零内存分配 专门针对List/数组的优化

Any(): 灵活但缓慢 适用于所有IEnumerable 有LINQ开销 更受青睐因为可读性更好

开发者们将这些经验教条般地植入代码库: "处理List一定要用Exists()!用Any()的都是菜鸟!"

.NET 9改变游戏规则

.NET 9运行时引入了多项有利于Any()的关键改进:

  1. LINQ方法内联 JIT编译器现在能积极内联像Any()这样的简单LINQ方法,减少虚方法调用开销,使Any()基础性能更接近直接方法调用。

  2. 基于Span的优化 改进的Span处理使Any()在可能情况下为数组和列表使用优化路径,避免完整IEnumerable枚举:

// .NET 9内部实现示意
public static bool Any<T>(this List<T> list, Func<T, bool> predicate)
{
    foreach (ref readonly var item in CollectionsMarshal.AsSpan(list))
    {
        if (predicate(item)) return true;
    }
    return false;
}
  1. 零分配 新的集合API利用ref struct枚举器,消除了常见场景下的堆分配

性能基准测试

让我们看看在.NET 9中的测试设置和结果对比

测试设置:

using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;

public class AnyVsExistsBenchmark
{
    private List<int> _list;
    private int[] _array;
    private IEnumerable<int> _enumerable;

    [Params(10, 1000, 1_000_000)]
    public int CollectionSize { get; set; }

    [GlobalSetup]
    public void Setup()
    {
        var random = new Random();
        _list = Enumerable.Range(0, CollectionSize)
            .Select(_ => random.Next(0, 10000))
            .ToList();
        
        _array = _list.ToArray();
        _enumerable = _list.Select(x => x);
    }

    [Benchmark(Description = "List.Any()")]
    public bool ListAny() => _list.Any(x => x > 5000);

    [Benchmark(Description = "List.Exists()")]
    public bool ListExists() => _list.Exists(x => x > 5000);

    [Benchmark(Description = "Array.Any()")]
    public bool ArrayAny() => _array.Any(x => x > 5000);

    [Benchmark(Description = "IEnumerable.Any()")]
    public bool EnumerableAny() => _enumerable.Any(x => x > 5000);

    public static void RunBenchmarks()
    {
        var summary = BenchmarkRunner.Run<AnyVsExistsBenchmark>();
        Console.WriteLine(summary);
    }
}

测试内容:

  • 比较List上的Any()和Exists()
  • 包含数组和普通IEnumerable上的Any()性能
  • 测试小(10)、中(1,000)、大(1,000,000)集合
  • 使用简单谓词(x > 5000),约半数情况能找到匹配

.NET 9中Any() vs Exists()基准测试

在.NET 9中,基准测试显示在枚举集合(如Array和List)中Any()和Exists()的差异约为2.5倍 - 在小集合和大集合中;然而中等大小集合的性能在Any()和Exists()之间相当。

现在,让我们切换回.NET 8再次检查结果:

.NET 8中Any() vs Exists()基准测试

如我们所见,.NET 8的Any()在所有测试中都明显落后。

何时仍需Exists()

  • 百万元素列表:Exists()在原始吞吐量上仍保持20-30%优势
  • 分配敏感场景:游戏引擎、高频交易
  • 遗留代码库:已有性能关键路径使用List

但关键是:对于10万元素以下的集合,你需要秒表才能察觉差异。

新现实检查

  1. 可读性取胜
// 清晰意图 vs 过早优化
if (users.Any(u => u.IsAdmin)) {...}  // 👍 推荐
if (users.Exists(u => u.IsAdmin)) {...}  // 🤔 "为什么不用Any?"
  1. LINQ统一性 Entity Framework Core 9现在将两种方法转为相同SQL:
-- 都转为
IF EXISTS(SELECT 1 FROM Users WHERE IsAdmin = 1)...
  1. 面向未来 随着.NET性能发展,Any()可能在未来版本中完全超越Exists()

2025实用建议

  • 新代码:默认使用Any(),除非处理超大List
  • 现有代码:仅在热点路径重构
  • 有疑问时:用dotnet benchmark测试你的具体案例
  • 团队规范:一致性 > 微观优化

"最好的性能优化是不需要做的优化" ——每个上线日后的资深开发者

最终结论

虽然Exists()在合成基准测试中仍保持微弱优势,但.NET 9已使这种差异对大多数应用失去实际意义。新的选择标准应倾向于:

  • 代码清晰度优于纳秒级优化
  • API一致性跨越集合类型
  • 可维护性面向未来开发者

所以下次当你纠结Exists()与Any()时,请记住:在.NET 9中,你的时间更应该投入实际业务逻辑优化,而非方法调用的选择。

相关留言评论
昵称:
邮箱:
阅读排行