SuperAPI AI 工具集

blazing-fast-dotnet

编程与开发

查看项目

更新于 10-28 01:57

技能介绍

Blazing Fast .NET 是一个专注于 .NET 应用性能优化的技能,旨在帮助开发者编写和调优高性能 C# 代码,实现极快的执行速度。该技能面向 .NET 10.0 和 11.0,旧版本未经过测试。技能开头给出警告:其中许多建议可能随 .NET 演进变得过时甚至有害,应在仔细考虑和测试后再应用,并定期复查以确保仍然适用。

用途

该技能用于在需要让 .NET 代码更快、进行性能调优或编写基准测试时提供指导。内容覆盖性能剖析、低层问题诊断、C# 编码最佳实践、ASP.NET Core 调优以及 NativeAOT 特定调优等。

主要能力

性能剖析与性能分析:建议从低成本工具开始,按需深入,顺序为 counters(判断瓶颈类型)→ traces(定位位置)→ perf 与 JIT 反汇编。涉及 dotnet-counters、dotnet-trace 以及 Linux perf 的低层剖析。

dotnet-counters 是低开销的一级分诊工具,可在应用负载下运行,支持 monitor 和 collect 命令,默认使用 System.Runtime 计数器,也可指定 System.Runtime、Microsoft.AspNetCore.Hosting、System.Net.Http 等。

dotnet-trace 可在任意操作系统上收集 EventPipe 跟踪,无需 root 或原生剖析器(仅托管帧),支持默认配置文件、gc-verbose、clrevents contention 等,可从启动时跟踪,并支持 topN 报告和转换为 Speedscope 格式。

Linux perf 低层剖析:主要针对 Linux 上的 CoreCLR/JIT 场景,需要设置 DOTNET_PerfMapEnabled=1、DOTNET_PerfMapStubGranularity=3、DOTNET_JitFramed=1、DOTNET_EnableWriteXorExecute=0 等环境变量。通常在进程预热后附加并收集,使用 perf record、perf inject --jit 符号化 JIT 帧,再生成火焰图或注释汇编。缺失符号时可用 dotnet-symbol 下载调试符号。

常见低层问题:包括多线程代码中的缓存争用与伪共享、JIT 未消除边界检查、存储到加载转发停顿、数据局部性差导致缓存未命中、代码对齐噪声、GC 的 8 字节对齐可能不适合 SIMD 优化等。

检查 JIT 代码生成:提供 DOTNET_JitDisasm、DOTNET_JitDisasmSummary、DOTNET_JitStdOutFile、DOTNET_JitDisasmDiffable、DOTNET_JitDisasmOnlyOptimized 等诊断开关,这些开关与发布的 Release 运行时配合使用。方法会随分层编译多次编译,应判断最后一个版本(Tier1 或 FullOpts)。NativeAOT 可通过 ILC 传递相同选项。

高性能 C# 最佳实践(仅考虑安全 C# 代码,不涉及 unsafe):

快速提示包括:使用源生成的 System.Text.Json;使用 [LoggerMessage] 源生成日志;使用 [GeneratedRegex];优先使用优化过的批量 API(Span.CopyTo、Clear、Fill、SequenceEqual、IndexOf 等);使用 RVA 支持的 span 存放不可变数据;热路径中优先使用静态 lambda 和状态传递重载;直接解析和格式化 UTF-8;缓存 SearchValues;考虑 FrozenDictionary/FrozenSet;使用字典的 span 替代查找;为结构体字典/集合键实现 IEquatable<T> 和良好的 GetHashCode;优先尝试 TensorPrimitives;优先使用 [LibraryImport] 而非 [DllImport];使用静态 readonly 标志;在热循环中将字段缓存到局部变量;考虑 JavaScriptEncoder.UnsafeRelaxedJsonEscaping;优先使用内置位/字节操作辅助方法;优先使用 MemoryExtensions 扩展方法;不要将基准测试固定到单个核心;AggressiveOptimization 通常是去优化;过度使用 AggressiveInlining 可能损害性能;不要向 TLS 放入过多数据。

字符串与全球化:优先使用序数操作。区分默认区分文化的操作(string.Compare、CompareTo、StartsWith(string)、EndsWith(string)、IndexOf(string)、LastIndexOf(string)、ToUpper()、ToLower() 及默认字符串排序)与已经是序数的操作(==、Equals、Contains(string)、Replace(string, string)、IndexOf(char)、Split、GetHashCode 及默认 Dictionary/HashSet 比较器)。不区分大小写相等应使用 string.Equals 配合 StringComparison.OrdinalIgnoreCase。Dictionary<string, T>/HashSet<string> 仅在默认比较器、StringComparer.Ordinal 或 StringComparer.OrdinalIgnoreCase 下使用快速非随机化哈希。输入输出为 UTF-8 时保持 UTF-8。若应用不需要文化数据,可使用 <InvariantGlobalization>true</InvariantGlobalization> 移除 ICU 依赖。

共享状态争用:多线程写入同一共享位置会在单个缓存行上串行化,核心越多吞吐可能越差。解决方案包括按核心分片(用 Thread.GetCurrentProcessorId() 索引数组,分片需填充)和 ThreadLocal<T>(trackAllValues: true)。若工作结构允许,使用 Parallel.For 的 localInit/localFinally 聚合并每任务合并一次更便宜。

伪共享:线程写入不同变量但共享缓存行时仍会争用(多数 x64/arm64 CPU 为 64 字节,Apple M 系列为 128 字节)。修复方法是填充每个槽位,使两个热值不会落在同一缓存行,例如使用 PaddedLong 结构配合 InlineArray8<long> 填充。

其他主题还包括:通过静态抽象成员进行泛型特化、ASP.NET Core、调优 .NET 性能的“魔法旋钮”、NativeAOT 特定调优。

使用方式

安装工具:

dotnet tool install -g dotnet-counters dotnet tool install -g dotnet-trace dotnet tool install -g dotnet-symbol

使用 dotnet-counters 监控或收集计数器,使用 dotnet-trace 收集跟踪并生成报告或转换格式,使用 Linux perf 进行低层剖析,使用 JIT 诊断开关检查代码生成。

适用场景

适用于需要优化 .NET 应用性能、编写高性能 C# 代码、进行基准测试(BenchmarkDotNet)、排查多线程争用与伪共享、分析 GC 与分配、调优 ASP.NET Core 或 NativeAOT 应用的场景。

限制

技能明确警告:许多建议可能随 .NET 演进过时或有害,需谨慎测试后应用并定期复查。目标框架为 .NET 10.0 和 11.0,旧版本未测试。最佳实践仅考虑安全 C# 代码,不涉及 unsafe 代码。Linux perf 说明主要针对 CoreCLR/JIT 场景,NativeAOT/R2R 场景假定可原样工作而无需 perf inject。样本归因不精确(“skid”),成本常报告在实际停顿指令稍后的指令上。