我需要测量同一台机器上两个进程之间的通信延迟。我想出的最好方法是将 DateTime.UtcNow
(DateTime.Now
似乎非常慢以至于它极大地扭曲了我的测量)到消息中并将其与 DateTime.UtcNow
进行比较在另一个过程中。这真的好吗?或者,还有更好的方法?
最佳答案
如果您的目标是测量和比较进程之间的准确时间,则应使用 Windows API 函数 QueryPerformanceCounter()
。它返回的值在进程之间同步,因为它返回一个内部处理器值。Stopwatch
在其实现中也使用 QueryPerformanceCounter()
,但它不公开返回的绝对值,因此您无法使用它。
你将不得不 use P/Invoke to call QueryPerformanceCounter() 但这很容易。
使用 P/Invoke 的开销很小。 From the MSDN documentation :
由于从 QueryPerformanceCounter()
返回的值很长,因此不会产生额外的编码(marshal)成本,因此您会留下 10-30 条指令的开销。
另请参阅 this MSDN blog,其中指出 UtcNow 的分辨率约为 10 毫秒 - 与性能计数器的分辨率相比,这是相当大的。 (虽然我实际上不相信这适用于 Windows 8;我的测量似乎表明 UtcNow 具有毫秒分辨率)。
无论如何,很容易证明 P/Invoking QueryPerformanceCounter() 比使用 DateTime.UtcNow 具有更高的分辨率。
如果您运行以下代码的发布版本(从调试器外部运行),您将看到几乎所有 DateTime.UtcNow 已用时间都是 0,而所有 QueryPerformanceCounter() 都非零。
这是因为 DateTime.UtcNow 的分辨率不足以测量调用 Thread.Sleep(0)
的耗时,而 QueryPerformanceCounter()
是。
using System;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApplication1
{
internal class Program
{
private static void Main(string[] args)
{
for (int i = 0; i < 100; ++i)
{
var t1 = DateTime.UtcNow;
Thread.Sleep(0);
var t2 = DateTime.UtcNow;
Console.WriteLine("UtcNow elapsed = " + (t2-t1).Ticks);
}
for (int i = 0; i < 100; ++i)
{
long q1, q2;
QueryPerformanceCounter(out q1);
Thread.Sleep(0);
QueryPerformanceCounter(out q2);
Console.WriteLine("QPC elapsed = " + (q2-q1));
}
}
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
}
}
现在我意识到可能是调用 QueryPerformanceCounter() 的开销如此之高,以至于它正在测量调用需要多长时间,而不是
Thread.Sleep(0)
需要多长时间。我们可以通过两种方式消除它:首先,我们可以修改第一个循环如下:
for (int i = 0; i < 100; ++i)
{
var t1 = DateTime.UtcNow;
long dummy;
QueryPerformanceCounter(out dummy);
Thread.Sleep(0);
QueryPerformanceCounter(out dummy);
var t2 = DateTime.UtcNow;
Console.WriteLine("UtcNow elapsed = " + (t2-t1).Ticks);
}
现在 UtcNow 应该计时 Thread.Sleep(0) 和两次调用 QueryPerformanceCounter()。但是如果你运行它,你仍然会看到几乎所有的耗时都是零。
其次,我们可以计算调用 QueryPerformanceCounter() 一百万次所需的时间:
var t1 = DateTime.UtcNow;
for (int i = 0; i < 1000000; ++i)
{
long dummy;
QueryPerformanceCounter(out dummy);
}
var t2 = DateTime.UtcNow;
Console.WriteLine("Elapsed = " + (t2-t1).TotalMilliseconds);
在我的系统上,调用 QueryPerformanceCounter() 一百万次大约需要 32 毫秒。
最后,我们可以计算调用 DateTime.UtcNow 一百万次所需的时间:
var t1 = DateTime.UtcNow;
for (int i = 0; i < 1000000; ++i)
{
var dummy = DateTime.UtcNow;
}
var t2 = DateTime.UtcNow;
Console.WriteLine("Elapsed = " + (t2-t1).TotalMilliseconds);
在我的系统上大约需要 10 毫秒,这比调用 QueryPerformanceCounter() 快大约 3 倍。
总结
因此 DateTime.UtcNow 的开销比 P/Invoking QueryPerformanceCounter() 低,但分辨率要低得多。
所以你付钱,你做你的选择!
关于c# - 跨进程测量时间,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16165109/