XPerf
语言C#

C# 性能指南(v0)

少做会重新分配的 list,让热路径更安静。

范围:现代 C#(.NET)。 加速数字会标明本地 bench、上游来源,或标为暂定。语料库残留计数是 lint 命中次数,不是墙钟耗时节省。

先画像(BenchmarkDotNet、dotnet-trace)。运行时与 GC 设置不同——请重测。

目录

1. List 与字符串

循环里的 List.Add 若不设 Capacity 会反复扩容。优先一次定容。紧密循环里的字符串 += 会分配;优先 StringBuilder。

每轮调用静态 Regex.IsMatch / Regex.Match 并传入模式字符串,都会在静态正则缓存里查找;优先复用一个 Regex 实例(热模式可用 RegexOptions.Compiled)。

Lint: cs-list-add-no-capacity, cs-string-plus-hot(默认 off); cs-regex-hot(默认 hint)

2. 热 I/O

紧密循环里的 Console.Write / WriteLine 很贵。门控调试输出或做缓冲。

Lint: cs-console-hot(默认 off)

3. 物化与格式化

循环里的 ToList / ToArray / ToDictionary 每轮分配新集合。循环里的 string.Format 每轮分配新字符串。循环里的 JsonSerializer.Serialize / Deserialize 与 JsonConvert.SerializeObject / DeserializeObject 每轮编解码。cs-json-hot 保持 hint。微基准是固定 payload。它不属于 2026-10-22 corpus-ready 裁决。

对同一路径 File.ReadAllText 在 .NET 10 上约 338×(相对读一次)。Stopwatch.GetTimestamp 约 37×(相对采一次样)。每轮 new Regex(两侧仍匹配)约 7×,与静态 Regex.IsMatch 同为 hint。变化的路径不能外提;需要新鲜时间戳的循环也没有可外提的东西。

每轮 GetField 再 GetValue 相对直接读字段约 62×。持有 FieldInfo 仍调用 GetValue 约 42×,因此只把 GetField 挪到循环外改动很小。每轮都变的名字不能变成直接字段。这与 Go 的 FieldByName 同族。

Lint: cs-linq-materialize-hot, cs-format-hot(默认 hint; pending measure); cs-json-hot, cs-regex-hot, cs-file-read-hot, cs-clock-hot, cs-field-by-name-hot(默认 hint)

Lint 对照

IDSmell默认
cs-list-add-no-capacity循环内无 Capacity 的 List.Addoff
cs-string-plus-hot循环内字符串 +=off
cs-console-hot循环内 Console.Write*off
cs-regex-hot循环内静态 Regex.IsMatch/Matchhint
cs-linq-materialize-hot循环内 ToList / ToArray / ToDictionaryhint(待测)
cs-format-hot循环内 string.Formathint(待测)
cs-json-hot循环内 JSON 编解码hint(有 JSONL,仅固定 payload)

仅在有 evidence/<rule>.jsonl 时升级——见 evidence-policy.md。

参考资料