ElapsedTicks更准,它返回原始计数器滴答数,配合Stopwatch.Frequency可换算纳秒级耗时;而ElapsedMilliseconds是截断小数的long型,丢失亚毫秒精度。

Stopwatch 是 C# 里唯一真正基于高性能计数器(HPET 或 TSC)的计时工具,精度远超 DateTime.Now 或 Environment.TickCount;但它的“高精度”只在你正确调用的前提下成立——误用 Start()/Stop() 顺序、跨线程读取 ElapsedMilliseconds、或在低权限容器中运行,都会让它退化成毫秒级甚至更差。
Stopwatch.ElapsedMilliseconds 和 ElapsedTicks 哪个更准?
ElapsedMilliseconds 是四舍五入后的 long,丢失亚毫秒信息;ElapsedTicks 返回原始计数器滴答数,配合 Stopwatch.Frequency 才能算出真实纳秒级耗时。实际性能分析必须用后者。
- 默认情况下
Stopwatch.Frequency是硬件计数器每秒滴答数(常见值如 2,500,000、10,000,000),不是 10,000,000(即 100ns 单位) - 计算纳秒:`(double)sw.ElapsedTicks * 1_000_000_000 / Stopwatch.Frequency`
- 若只比对相对耗时(比如 A 函数比 B 慢多少),直接比
ElapsedTicks最快最稳,避免浮点误差
为什么反复调用 Start()/Stop() 后 ElapsedMilliseconds 跳变或归零?
Stopwatch 不是“重置即清零”,而是状态机:未启动时调用 Stop() 会抛 InvalidOperationException;已停止后再次 Start() 是**继续计时**,不是从零开始。常见错误写法:
sw.Start(); DoWork(); sw.Stop(); // …… 中间没 Reset() sw.Start(); // 错!这是接着上次停的位置继续跑
- 需要重新计时,必须显式调用
sw.Reset()或sw.Restart()(.NET 4.6+ 推荐后者,原子操作) - 多轮测量建议用新实例:
var sw = Stopwatch.StartNew();,避免状态残留 - 不要在
Stop()后还读Elapsed属性——虽然不报错,但若之后又Start(),该值不会更新
在 Docker 容器或某些虚拟机里 Stopwatch 精度暴跌怎么办?
部分旧版 Hyper-V、VMware 或低权限容器(如 rootless Podman)会禁用 TSC 或强制使用 HPET,导致 Stopwatch.IsHighResolution 返回 false,此时 Frequency 可能低至 1000(即毫秒级)。这不是代码问题,是运行时环境限制。
- 上线前务必检查:
Console.WriteLine($"HR: {Stopwatch.IsHighResolution}, Freq: {Stopwatch.Frequency}"); - Docker 需加
--cap-add=SYS_TIME(Linux)或启用host-time(Windows WSL2) - .NET 6+ 在容器中默认启用
System.Diagnostics.Process.EnableHighResolutionTimer,但需确认 runtime 版本与 host 兼容性
Stopwatch 的“高精度”本质是暴露硬件能力,它不负责兜底。真正难的不是调用 API,而是理解你的 CPU、hypervisor、OS kernel 和 .NET runtime 四层之间如何协商计时源——漏掉任何一层,ElapsedTicks 就只是个好看的大数字。











