我需要找到一个瓶颈,并且需要尽可能准确地测量时间。
以下代码片段是衡量性能的最佳方法吗?
DateTime startTime = DateTime.Now;
// Some execution process
DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
我需要找到一个瓶颈,并且需要尽可能准确地测量时间。
以下代码片段是衡量性能的最佳方法吗?
DateTime startTime = DateTime.Now;
// Some execution process
DateTime endTime = DateTime.Now;
TimeSpan totalTimeTaken = endTime.Subtract(startTime);
不,这不对。使用秒表(in System.Diagnostics
)
Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();
Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);
秒表会自动检查是否存在高精度计时器。
值得一提的是,由于必须使用时区、DST等来完成工作,因此DateTime.Now
通常要慢很多。DateTime.UtcNow
DateTime.UtcNow 通常具有 15 毫秒的分辨率。请参阅John Chapman 的关于精度的博客文章以获得很好的总结。DateTime.Now
DateTime.UtcNow
有趣的琐事:如果您的硬件不支持高频计数器,秒表就会重新启动。您可以通过查看静态字段Stopwatch.IsHighResolution来检查 Stopwatch 是否使用硬件来实现高精度。
如果您想要快速而肮脏的东西,我建议您使用秒表来获得更高的精度。
Stopwatch sw = new Stopwatch();
sw.Start();
// Do Work
sw.Stop();
Console.WriteLine("Elapsed time: {0}", sw.Elapsed.TotalMilliseconds);
或者,如果您需要更复杂的东西,您可能应该考虑使用 3rd 方分析器,例如ANTS。
这篇文章说,首先你需要比较三个备选方案Stopwatch
,DateTime.Now
和DateTime.UtcNow
。
它还表明在某些情况下(当性能计数器不存在时)秒表正在使用 DateTime.UtcNow + 一些额外的处理。因此,很明显在这种情况下 DateTime.UtcNow 是最好的选择(因为其他人使用它+一些处理)
但是,事实证明,计数器几乎总是存在 - 请参阅关于高分辨率性能计数器及其存在与 .NET 秒表相关的说明?.
这是一个性能图表。请注意 UtcNow 与替代方案相比的性能成本有多低:
X 轴是样本数据大小,Y 轴是样本的相对时间。
更好的一件事Stopwatch
是它提供了更高分辨率的时间测量。另一个是它更多的面向对象性质。但是,创建一个面向对象的包装器UtcNow
并不难。
将您的基准测试代码推送到实用程序类/方法中很有用。该类StopWatch
不需要是Disposed
或Stopped
错误的。因此,计时某些操作的最简单代码是
public partial class With
{
public static long Benchmark(Action action)
{
var stopwatch = Stopwatch.StartNew();
action();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
示例调用代码
public void Execute(Action action)
{
var time = With.Benchmark(action);
log.DebugFormat(“Did action in {0} ms.”, time);
}
这是扩展方法版本
public static class Extensions
{
public static long Benchmark(this Action action)
{
return With.Benchmark(action);
}
}
和示例调用代码
public void Execute(Action action)
{
var time = action.Benchmark()
log.DebugFormat(“Did action in {0} ms.”, time);
}
使用 System.Diagnostics.Stopwatch 类。
Stopwatch sw = new Stopwatch();
sw.Start();
// Do some code.
sw.Stop();
// sw.ElapsedMilliseconds = the time your "do some code" took.
同上秒表,它更好。
关于性能测量,您还应该检查您的“// Some Execution Process”是否是一个非常短的过程。
另请记住,“// Some Execution Process”的第一次运行可能比后续运行慢得多。
我通常通过在循环中运行 1000 次或 1000000 次来测试一个方法,我得到的数据比运行一次更准确。
这些都是衡量时间的好方法,但这只是找到瓶颈的一种非常间接的方法。
在线程中找到瓶颈的最直接方法是让它运行,当它在做任何让你等待的事情时,用暂停或中断键停止它。这样做几次。如果您的瓶颈花费了 X% 的时间,那么 X% 是您在每个快照的行为中捕获它的概率。
仅供参考,.NET Timer 类不用于诊断,它以预设的时间间隔生成事件,如下所示(来自MSDN):
System.Timers.Timer aTimer;
public static void Main()
{
// Create a timer with a ten second interval.
aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
// Set the Interval to 2 seconds (2000 milliseconds).
aTimer.Interval = 2000;
aTimer.Enabled = true;
Console.WriteLine("Press the Enter key to exit the program.");
Console.ReadLine();
}
// Specify what you want to happen when the Elapsed event is
// raised.
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
}
所以这真的不能帮助你知道某件事花了多长时间,只是已经过去了一定的时间。
计时器也在 System.Windows.Forms 中作为控件公开...您可以在 VS05/VS08 的设计器工具箱中找到它
这是正确的方法:
using System;
using System.Diagnostics;
class Program
{
public static void Main()
{
Stopwatch stopWatch = Stopwatch.StartNew();
// some other code
stopWatch.Stop();
// this not correct to get full timer resolution
Console.WriteLine("{0} ms", stopWatch.ElapsedMilliseconds);
// Correct way to get accurate high precision timing
Console.WriteLine("{0} ms", stopWatch.Elapsed.TotalMilliseconds);
}
}
有关更多信息,请通过使用 Stopwatch 而不是 DataTime 来获得准确的性能计数器。
Visual Studio Team System具有一些可能有助于解决此问题的功能。本质上,您可以编写单元测试并将它们混合在不同的场景中,以作为压力或负载测试的一部分针对您的软件运行。这可能有助于识别对应用程序性能影响最大的代码区域。
Microsoft 的 Patterns and Practices 小组在Visual Studio Team System Performance Testing Guidance中有一些指导。
我刚刚在 Vance Morrison 的博客中发现了一篇关于他编写的 CodeTimer 类的帖子,该类使使用StopWatch
变得更容易,并且在旁边做了一些简洁的事情。
我在程序中使用的方式是使用 StopWatch 类,如此处所示。
Stopwatch sw = new Stopwatch();
sw.Start();
// Critical lines of code
long elapsedMs = sw.Elapsed.TotalMilliseconds;
我很少做这种性能检查(我倾向于认为“这很慢,让它更快”)所以我几乎总是这样做。
谷歌确实揭示了很多用于性能检查的资源/文章。
许多人提到使用 pinvoke 来获取性能信息。我研究的很多材料只真正提到了使用 perfmon ..
看过秒表的谈话..很好!我学到了一些东西:)
这还不够专业:
Stopwatch sw = Stopwatch.StartNew();
PerformWork();
sw.Stop();
Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds);
更可靠的版本是:
PerformWork();
int repeat = 1000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < repeat; i++)
{
PerformWork();
}
sw.Stop();
Console.WriteLine("Time taken: {0}ms", sw.Elapsed.TotalMilliseconds / repeat);
在我的真实代码中,我将添加 GC.Collect 调用以将托管堆更改为已知状态,并添加 Sleep 调用,以便在 ETW 配置文件中轻松分离不同的代码间隔。
因为我不太关心精度,所以我最终比较了它们。我正在网络上捕获大量数据包,我想确定接收每个数据包的时间。这是测试 500 万次迭代的代码
int iterations = 5000000;
// Test using datetime.now
{
var date = DateTime.UtcNow.AddHours(DateTime.UtcNow.Second);
var now = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
if (date == DateTime.Now)
Console.WriteLine("it is!");
}
Console.WriteLine($"Done executing {iterations} iterations using datetime.now. It took {(DateTime.UtcNow - now).TotalSeconds} seconds");
}
// Test using datetime.utcnow
{
var date = DateTime.UtcNow.AddHours(DateTime.UtcNow.Second);
var now = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
if (date == DateTime.UtcNow)
Console.WriteLine("it is!");
}
Console.WriteLine($"Done executing {iterations} iterations using datetime.utcnow. It took {(DateTime.UtcNow - now).TotalSeconds} seconds");
}
// Test using stopwatch
{
Stopwatch sw = new Stopwatch();
sw.Start();
var now = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
if (sw.ElapsedTicks == DateTime.Now.Ticks)
Console.WriteLine("it is!");
}
Console.WriteLine($"Done executing {iterations} iterations using stopwatch. It took {(DateTime.UtcNow - now).TotalSeconds} seconds");
}
输出是:
Done executing 5000000 iterations using datetime.now. It took 0.8685502 seconds
Done executing 5000000 iterations using datetime.utcnow. It took 0.1074324 seconds
Done executing 5000000 iterations using stopwatch. It took 0.9625021 seconds
所以总而言之 DateTime.UtcNow 如果你不太关心精度的话是最快的。这也支持这个问题的答案https://stackoverflow.com/a/6986472/637142。