从入门到进阶:C#.NET Stopwatch 计时与性能测量全攻略

简介

Stopwatch.NET 提供的高精度计时器,位于 System.Diagnostics 命名空间。

底层依赖操作系统的高分辨率性能计数器(Performance Counter),当硬件支持时,其精度可达纳秒级;在不支持时退回到环境时钟(毫秒级)。

主要用途包括:

  • 性能测量(Benchmark

  • 代码片段耗时分析

  • 定时任务、超时检测

核心原理与特性

工作原理

Stopwatch 使用底层硬件计时器(通常是 CPU 的高分辨率性能计数器)提供纳秒级精度的时间测量:

graph LR A[应用程序] --> B[Stopwatch] B --> C[QueryPerformanceCounter API] C --> D[硬件计时器] D --> E[高精度时间测量]

关键特性

  • 高精度:最高可达纳秒级(取决于硬件)

  • 低开销:专门优化的计时实现

  • 独立计时:不受系统时间调整影响

  • 线程安全:可在多线程环境中安全使用

核心属性

属性 类型 描述
IsRunning bool 指示计时器是否正在运行
Elapsed TimeSpan 当前测量的总时间
ElapsedMilliseconds long 总毫秒数(整数)
ElapsedTicks long 计时器滴答计数
Frequency static long 每秒的计时器滴答数

核心 API

成员 说明
Start() 启动或继续计时(若已停止,则接着累加)。
Stop() 暂停计时,保留已累计时间。
Reset() 重置累计时间为零,但不改变运行状态(运行中依旧在计时)。
Restart() 等同于 Reset() + Start(),常用于重复测量。
IsRunning (bool) 当前是否在运行状态。
Elapsed 返回 TimeSpan 类型的累计运行时间。
ElapsedMilliseconds 累计运行总毫秒(取整)。
ElapsedTicks 累计运行计数器刻度数(与硬件计时器相关)。
静态属性 Frequency 性能计数器频率(每秒刻度数),用于将刻度转换为时间; IsHighResolution 表示是否为高分辨率计数器。

典型用法

基本测量

csharp 复制代码
var sw = Stopwatch.StartNew();    // 等同于 new Stopwatch(); sw.Start();
DoWork();
sw.Stop();
Console.WriteLine($"耗时:{sw.Elapsed.TotalMilliseconds} ms");

分段测量(Laps)

csharp 复制代码
var sw = new Stopwatch();
sw.Start();

// 第一段
DoStep1();
Console.WriteLine($"Step1: {sw.ElapsedMilliseconds} ms");

// 第二段
DoStep2();
Console.WriteLine($"Step2: {sw.ElapsedMilliseconds} ms");

// 重置累计,再测
sw.Restart();
DoOther();
Console.WriteLine($"Other: {sw.Elapsed.TotalMilliseconds} ms");

多次重复测量

csharp 复制代码
var sw = new Stopwatch();
for (int i = 0; i < 5; i++)
{
    sw.Restart();
    DoWork();
    Console.WriteLine($"Run {i}: {sw.ElapsedMilliseconds} ms");
}

精确时间计算

csharp 复制代码
// 纳秒级计算
long start = Stopwatch.GetTimestamp();
// 执行操作...
long end = Stopwatch.GetTimestamp();
double elapsedNs = (end - start) * 1_000_000_000 / (double)Stopwatch.Frequency;

进阶技巧

测量异步方法

csharp 复制代码
var sw = Stopwatch.StartNew();
await SomeAsyncOperation();
sw.Stop();
Console.WriteLine($"Async 耗时:{sw.ElapsedMilliseconds} ms");

嵌套测量

csharp 复制代码
var swTotal = Stopwatch.StartNew();
var swA = new Stopwatch(); swA.Start();
DoA(); swA.Stop();
var swB = Stopwatch.StartNew();
DoB(); swB.Stop();
swTotal.Stop();
Console.WriteLine($"A: {swA.Elapsed}  B: {swB.Elapsed}  Total: {swTotal.Elapsed}");

避免 GC 干扰

  • 对短时测量可先进行一次"热身"(预调用方法和计时器)以加载 JIT、内存分配等。

  • 可以在测量前后调用 GC.Collect(); GC.WaitForPendingFinalizers();,以减少 GC 的随机停顿对结果的影响。

性能测试框架

csharp 复制代码
public static TimeSpan MeasurePerformance(Action action, int iterations = 1000)
{
    // 预热
    action();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    
    var sw = Stopwatch.StartNew();
    for (int i = 0; i < iterations; i++)
    {
        action();
    }
    sw.Stop();
    
    return TimeSpan.FromTicks(sw.ElapsedTicks / iterations);
}

// 使用示例
var avgTime = MeasurePerformance(() => 
{
    // 测试的代码块
    var result = Enumerable.Range(0, 1000).Sum();
}, 10000);

Console.WriteLine($"平均执行时间: {avgTime.TotalMilliseconds:F6} ms");

高级应用场景

性能监控中间件

csharp 复制代码
public class PerformanceMonitorMiddleware
{
    private readonly RequestDelegate _next;
    
    public PerformanceMonitorMiddleware(RequestDelegate next)
    {
        _next = next;
    }
    
    public async Task Invoke(HttpContext context)
    {
        var sw = Stopwatch.StartNew();
        
        await _next(context);
        
        sw.Stop();
        context.Response.Headers["X-Execution-Time"] = sw.ElapsedMilliseconds.ToString();
        
        // 记录到日志系统
        LogPerformance(context.Request.Path, sw.Elapsed);
    }
}

// 在Startup中注册
app.UseMiddleware<PerformanceMonitorMiddleware>();

算法复杂度分析

csharp 复制代码
public void AnalyzeAlgorithmComplexity()
{
    for (int n = 1000; n <= 1000000; n *= 10)
    {
        int[] data = Enumerable.Range(0, n).ToArray();
        
        var sw = Stopwatch.StartNew();
        Array.Sort(data); // 测试排序算法
        sw.Stop();
        
        Console.WriteLine($"n={n}, 时间={sw.ElapsedTicks} ticks");
    }
}
相关推荐
私人珍藏库3 小时前
[Windows] 微软 .Net 运行库离线安装包 | Microsoft .Net Packages AIO_v09.09.25
microsoft·.net·运行库
未来之窗软件服务11 小时前
幽冥大陆(二)RDIFSDK 接口文档:布草洗涤厂高效运营的技术桥梁C#—东方仙盟
开发语言·c#·rdif·仙盟创梦ide·东方仙盟
1uther11 小时前
Unity核心概念⑨:Screen
开发语言·游戏·unity·c#·游戏引擎
追逐时光者12 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 54 期(2025年9.8-9.14)
后端·.net
追逐时光者12 小时前
C#/.NET/.NET Core编程技巧练习集,配套详细的文章教程讲解!
后端·.net
阿幸软件杂货间12 小时前
Office转PDF转换器v1.0.py
开发语言·pdf·c#
sali-tec13 小时前
C# 基于halcon的视觉工作流-章34-环状测量
开发语言·图像处理·算法·计算机视觉·c#
Tiger_shl13 小时前
【层面一】C#语言基础和核心语法-02(反射/委托/事件)
开发语言·c#
mudtools17 小时前
.NET驾驭Word之力:COM组件二次开发全攻略之连接Word与创建你的第一个自动化文档
后端·c#