从入门到进阶: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 小时前
C#基础10-结构体和枚举
java·开发语言·c#
SEO-狼术11 小时前
.NET WPF 数据编辑器集合提供列表框控件
.net·wpf
SEO-狼术15 小时前
Oxygen AI Positron Assistant Enterprise
人工智能·.net
惜.己1 天前
jmeter中java.net.ConnectException: Connection refused: connect
java·jmeter·.net
secondyoung1 天前
Markdown转换为Word:Pandoc模板使用指南
开发语言·经验分享·笔记·c#·编辑器·word·markdown
andyguo1 天前
AI模型测评平台工程化实战十二讲(第五讲:大模型测评分享功能:安全、高效的结果展示与协作)
人工智能·安全·c#
关关长语2 天前
Dotnet接入AI通过Response创建一个简单控制台案例
人工智能·.net·ai dotnet
大飞pkz2 天前
【设计模式】访问者模式
开发语言·设计模式·c#·访问者模式
LateFrames2 天前
用 【C# + Winform + MediaPipe】 实现人脸468点识别
python·c#·.net·mediapipe
追逐时光者2 天前
精选 4 款开源免费、美观实用的 MAUI UI 组件库,助力轻松构建美观且功能丰富的应用程序!
后端·.net