返回首页

.NET 8 中的 SearchValues 和 FrozenCollections 优化

本文详解 .NET 8 中的 SearchValues 和 FrozenCollections,用于加速热路径搜索。提供基准测试(字符串 13 倍,字典 2 倍)和代码示例。读密集型场景的使用建议。

使用 SearchValues 在 .NET 8 中实现 13 倍搜索加速
Advertisement 728x90

.NET 8 搜索优化:SearchValues 和 FrozenCollections 加速热路径

.NET 8 新增了 SearchValues<T> 以及 FrozenSet<T>/FrozenDictionary<TKey, TValue> 等集合类型,它们大幅降低热路径开销。这些结构在创建时完成繁重计算,提供常量时间查找,无需重复分析。非常适合处理数百万操作的高吞吐量应用。

用 SearchValues 加速字符搜索

传统的 string.IndexOfAny 每次调用都会重新分析字符集,在循环中积累大量开销。SearchValues<char> 只需创建一次,就能缓存最优搜索策略。

初始化时,.NET 会执行:

Google AdInline article slot
  • 输入集分析(单字符、范围或多字符)。
  • 支持 SIMD(AVX2、SSE2)的算法选择。
  • 构建数据结构(位掩码、哈希表)。
SearchValues<char> separators = SearchValues.Create(".,;!?");
ReadOnlySpan<char> input = "Hello, world!";
int index = input.IndexOfAny(separators);

搜索策略根据数据类型而变:

  • 单字符 — 向量化检查。
  • 范围(0-9A-Z) — O(1) 位掩码。
  • 小集合(≤4 个字符) — SIMD。
  • 大集合 — 优化的哈希表。

基准测试显示显著提升:

| 场景 | IndexOfAny | SearchValues | 加速比 |

Google AdInline article slot

|----------|------------|--------------|---------|

| 短字符串 | ~5 ns | ~5 ns | 1x |

| 长字符串(2000 字符) | ~52 ns | ~4 ns | ~13x |

Google AdInline article slot

对超过 100 字符的长字符串,收益最大,避免重复分析。

FrozenCollections:不可变哈希表

HashSet<T>Dictionary<TKey, TValue> 带有变异开销,如重哈希和边界检查。FrozenSet<T>FrozenDictionary<TKey, TValue> 专为只读场景优化。

创建时:

  • 键分析,生成零冲突的完美哈希函数。
  • 缓存友好的数据布局。
  • 去除运行时不可变性检查。

小集合使用位掩码替代哈希。

var dict = Enumerable.Range(0, 100000)
    .ToFrozenDictionary(x => x, x => $"Value {{x}}");

if (dict.TryGetValue(42, out string? value))
{
    Console.WriteLine(value);
}

基准测试(10 万元素):

| 操作 | Dictionary | HashSet | FrozenDictionary | FrozenSet |

|-----------|------------|---------|------------------|-----------|

| TryGetValue/Contains | 3.78 ns | 4.47 ns | 1.87 ns | 3.37 ns |

FrozenDictionary 比标准 Dictionary 快一倍。

实际应用场景

SearchValues:

  • 针对固定集的数百万次查找。
  • 日志解析、分词、验证。
  • 长字符串(>100 字符)。

FrozenCollections:

  • 启动时加载的静态配置。
  • 查找表(错误码、状态)。
  • 读密集型工作负载,数千次查找。

频繁变异或罕见调用时避免使用——创建成本会抵消收益。

实用实现

在应用级别缓存:

public static class AppCache
{
    public static readonly SearchValues<char> UrlSafeChars = 
        SearchValues.Create("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~");
    
    public static readonly FrozenSet<string> ValidCurrencies = 
        new[] { "USD", "EUR", "GBP", "JPY", "CNY" }.ToFrozenSet();
}

验证示例:

  • 邮箱:SearchValues<char> invalidEmailChars = SearchValues.Create("()<>[]:;@\\,?\" ");
  • 顶级域名:FrozenSet<string> validTlds = LoadTldsFromConfig().ToFrozenSet();

始终用 BenchmarkDotNet 性能分析——收益取决于操作量。

核心要点

  • SearchValues 通过一次性优化,让长字符串搜索加速 13 倍。
  • FrozenDictionaryTryGetValue 性能是 Dictionary 的两倍。
  • 两者在热路径的不可变集上大放异彩。
  • 先性能分析:不频繁调用无用。
  • 利用 SIMD 和完美哈希,中高级开发者必备。

— Editorial Team

Advertisement 728x90

继续阅读