从入门到进阶: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");
    }
}
相关推荐
He少年3 小时前
【AI 辅助案例分享】
人工智能·c#·编辑器·ai编程
工程师0075 小时前
栈和堆的概念
c#·栈和堆
不会编程的懒洋洋5 小时前
C# P/Invoke 基础
开发语言·c++·笔记·安全·机器学习·c#·p/invoke
Avalon7126 小时前
Unity3D响应式渲染UI框架UniVue
游戏·ui·unity·c#·游戏引擎
njsgcs6 小时前
solidworks折弯自动标注5 非90度折弯
c#·solidworks
狼与自由6 小时前
clickhouse引擎
clickhouse·c#·linq
wangnaisheng7 小时前
【C#】死锁详解:发生原因、优化解决方案
c#
tiger从容淡定是人生7 小时前
AI替代软件战略(一):从 CCleaner 到 MCP 架构重构 —— TigerCleaner 的工程实践
人工智能·重构·架构·c#·mcp
周杰伦fans19 小时前
AutoCAD .NET 二次开发:深入理解 EntityJig 的工作原理与正确实现
开发语言·.net
宝桥南山1 天前
GitHub Models - 尝试一下使用GitHub Models
microsoft·ai·微软·c#·github·.netcore