这是该线程中可能是最有用的答案和评论的更新和刷新摘要+额外的基准和变体:
首先第一件事:正如其他人在评论中指出的那样,过去几年情况发生了变化,“现代”Windows (Win XP ++) 和 .NET 以及现代硬件没有或很少有理由不使用 Stopwatch()。有关详细信息,请参阅MSDN。语录:
“QPC 精度是否会受到电源管理或 Turbo Boost 技术引起的处理器频率变化的影响?不会
。如果处理器具有不变的 TSC,则 QPC 不受这些变化的影响。如果处理器没有不变的 TSC, QPC 将恢复为不受处理器频率变化或 Turbo Boost 技术影响的平台硬件计时器。
QPC 在多处理器系统、多核系统和超线程系统上是否可靠工作?
是的
如何确定和验证 QPC 在我的机器上工作?
您无需执行此类检查。
哪些处理器具有非不变 TSC?[..进一步阅读..]“
但是,如果您不需要 Stopwatch() 的精度,或者至少想确切了解 Stopwatch 的性能(静态与基于实例)和其他可能的变体,请继续阅读:
我从 cskwg 接管了上面的基准测试,并为更多变体扩展了代码。我用几年前的 i7 4700 MQ 和 C# 7 和 VS 2017 进行了测量(更准确地说,用 .NET 4.5.2 编译,尽管是二进制文字,它是 C# 6(使用这个:字符串文字和'使用静态'). 与上述基准相比,尤其是 Stopwatch() 的性能似乎有所提高。
这是一个循环中 1000 万次重复结果的示例,与往常一样,绝对值并不重要,但即使是相对值也可能在其他硬件上有所不同:
32位,无优化的发布模式:
测量:GetTickCount64() [ms]:275
测量:Environment.TickCount [ms]:45
测量:DateTime.UtcNow.Ticks [ms]:167
测量:秒表:.ElapsedTicks [ms]:277
测量:秒表:.ElapsedMilliseconds [ ms]:548
测量:静态 Stopwatch.GetTimestamp [ms]:193
测量:秒表+转换为 DateTime [ms]:551
与 DateTime.Now.Ticks [ms] 比较:9010
32 位,发布模式,优化:
实测:GetTickCount64() [ms]:198
实测:Environment.TickCount [ms]:39
实测:DateTime.UtcNow.Ticks [ms]:66 (!)
实测:秒表:.ElapsedTicks [ms]:175
实测:秒表: .ElapsedMilliseconds [ms]:491
测量:静态 Stopwatch.GetTimestamp [ms]:175
测量:秒表+转换为 DateTime [ms]: 510
与 DateTime.Now.Ticks [ms] 比较:8460
64位,无优化的发布模式:
测量:GetTickCount64() [ms]:205
测量:Environment.TickCount [ms]:39
测量:DateTime.UtcNow.Ticks [ms]:127
测量:秒表:.ElapsedTicks [ms]:209
测量:秒表:.ElapsedMilliseconds [ ms]:285
测量:静态 Stopwatch.GetTimestamp [ms]:187
测量:秒表+转换为 DateTime [ms]:319
与 DateTime.Now.Ticks [ms] 比较:3040
64 位,发布模式,优化:
实测:GetTickCount64() [ms]:148
实测:Environment.TickCount [ms]:31 (还值得吗?)
实测:DateTime.UtcNow.Ticks [ms]:76 (!)
实测:秒表:.ElapsedTicks [ ms]:178
测量:秒表:.ElapsedMilliseconds [ms]:226
测量:静态 Stopwatch.GetTimestamp [ms]:175
测量:秒表+转换为 DateTime [ms]:246
与 DateTime.Now.Ticks [ms] 进行比较: 3020
可能非常有趣的是,创建一个 DateTime 值来打印出秒表时间似乎几乎没有成本。有趣的是,静态秒表的速度比实际要快一些(正如预期的那样)。一些优化点非常有趣。例如,我无法解释为什么只有 32 位的 Stopwatch.ElapsedMilliseconds 与其他变体相比如此慢,例如静态变体。这和 DateTime.Now 在 64 位上的速度是其速度的两倍多。
您可以看到:仅对于数百万次处决,秒表的时间才开始变得重要。如果情况确实如此(但要提防过早进行微优化),使用 GetTickCount64() 可能会很有趣,但尤其是使用DateTime.UtcNow时,您有一个 64 位(长)计时器,其精度低于秒表,但速度更快,这样您就不必乱用 32 位“丑陋”的 Environment.TickCount。
正如预期的那样,DateTime.Now 是迄今为止最慢的。
如果您运行它,代码还会检索您当前的秒表精度等。
这是完整的基准代码:
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Environment;
[...]
[DllImport("kernel32.dll") ]
public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start
static void Main(string[] args)
{
const int max = 10_000_000;
const int n = 3;
Stopwatch sw;
// Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx
// But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading
Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Thread.Sleep(2); // warmup
Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}");
for (int j = 0; j < n; j++)
{
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var tickCount = GetTickCount64();
}
sw.Stop();
Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days
}
sw.Stop();
Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = DateTime.UtcNow.Ticks;
}
sw.Stop();
Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = sw.ElapsedMilliseconds;
}
sw.Stop();
Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}");
//
//
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = Stopwatch.GetTimestamp();
}
sw.Stop();
Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}");
//
//
DateTime dt=DateTime.MinValue; // just init
sw = new Stopwatch();
sw.Start();
for (int i = 0; i < max; i++)
{
var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference
}
sw.Stop();
//Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}");
Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]: {sw.ElapsedMilliseconds}");
Console.WriteLine();
}
//
//
sw = new Stopwatch();
var tickCounterStart = Environment.TickCount;
sw.Start();
for (int i = 0; i < max/10; i++)
{
var a = DateTime.Now.Ticks;
}
sw.Stop();
var tickCounter = Environment.TickCount - tickCounterStart;
Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}");
Console.WriteLine($"{NewLine}General Stopwatch information:");
if (Stopwatch.IsHighResolution)
Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
else
Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
double freq = (double)Stopwatch.Frequency;
double ticksPerMicroSec = freq / (1000d*1000d) ; // microsecond resolution: 1 million ticks per sec
Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}");
Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)");
DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L); // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan
Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}");
// this conversion from seems not really accurate, it will be between 24-25 days.
Console.WriteLine($"{NewLine}Done.");
while (Console.KeyAvailable)
Console.ReadKey(false);
Console.ReadKey();
}