← 全部文章 C# 语言

C# 计算代码运行时间

2021 年 6 月 9 日 · 1 分钟读完

目录
  1. 方法一:Stopwatch
  2. 方法二:DateTime 相减
  3. 该用哪个
  4. 延伸

方法一:Stopwatch

System.Diagnostics.Stopwatch 专门用来准确测量运行时间:

  1. 创建一个 Stopwatch;
  2. Start() 开始计时;
  3. Stop() 停止计时;
  4. ElapsedMilliseconds 取经过的毫秒数。
using System;
using System.Diagnostics;
using System.Threading;

var sw = new Stopwatch();
sw.Start();
Thread.Sleep(5000);
sw.Stop();
Console.Write("运行的时间为:" + sw.ElapsedMilliseconds);

实际运行多出 3 ms

Sleep(5000) 实际测出来多了几毫秒,这是线程调度的误差,Sleep 只保证「至少」睡这么久。

方法二:DateTime 相减

两次 DateTime.Now 相减得到一个 TimeSpan:

DateTime dt = DateTime.Now;
Thread.Sleep(5000);
DateTime dt2 = DateTime.Now;
TimeSpan ts = dt2 - dt;
Console.Write("运行的时间为:" + ts);                    // 00:00:05.0039874
Console.Write("运行的时间为:" + ts.TotalMilliseconds);  // 按毫秒显示

该用哪个

测耗时用 Stopwatch,记录时刻用 DateTime。 原因:

  • DateTime.Now 的精度依赖系统时钟,在一些 Windows 机器上只有 10~16 ms 左右,测短操作很不准;Stopwatch 底层用的是高精度性能计数器(QueryPerformanceCounter),精度通常在微秒以下。
  • DateTime.Now 会受系统改时间、NTP 校时、夏令时影响,两次取值之间系统时间被改了,差值就是错的;Stopwatch 只往前走。
  • DateTime.Now 还要换算本地时区,比 DateTime.UtcNow 慢。

延伸

  • 更简洁的写法:var sw = Stopwatch.StartNew(); 一步创建并开始。.NET 7 起可以不创建对象:long start = Stopwatch.GetTimestamp(); ... TimeSpan elapsed = Stopwatch.GetElapsedTime(start);,适合高频调用的地方。
  • Elapsed 比 ElapsedMilliseconds 精确:后者是取整的毫秒,前者是 TimeSpan,sw.Elapsed.TotalMilliseconds 能看到小数。
  • 认真比较性能用 BenchmarkDotNet:只跑一次的计时会受 JIT 编译、缓存、GC 影响,第一次总是最慢。BenchmarkDotNet 会自动预热、多次运行、统计误差,还能报告内存分配,写个 [Benchmark] 特性就行,一定要在 Release 模式下跑。
  • 在工控软件里记录节拍(CT):每个工位开始和结束各记一个 Stopwatch.GetTimestamp(),比 DateTime 稳定得多,不会因为工控机校时出现负数节拍。

# C# # 性能

原文首发于 blog.csdn.net