gpt4 book ai didi

c# - 跨进程测量时间

转载 作者:行者123 更新时间:2023-11-30 12:30:22 28 4
gpt4 key购买 nike

我需要测量同一台机器上两个进程之间的通信延迟。我想出的最好方法是序列化 DateTime.UtcNow(DateTime.Now 似乎非常慢以至于它极大地扭曲了我的测量)到消息中并将其与另一个进程中的 DateTime.UtcNow 进行比较。这是尽善尽美吗?或者有更好的方法吗?

最佳答案

如果您的目标是测量和比较进程之间的准确时间,您应该使用 Windows API 函数 QueryPerformanceCounter() .它返回的值在进程之间同步,因为它返回一个内部处理器值。

Stopwatch 在其实现中也使用了 QueryPerformanceCounter(),但它不会公开返回的绝对值,因此您不能使用它。

你必须use P/Invoke to call QueryPerformanceCounter()但这很容易。

使用P/Invoke 的开销很小。 From the MSDN documentation :

PInvoke has an overhead of between 10 and 30 x86 instructions per call. In addition to this fixed cost, marshaling creates additional overhead. There is no marshaling cost between blittable types that have the same representation in managed and unmanaged code. For example, there is no cost to translate between int and Int32.

由于从 QueryPerformanceCounter() 返回的值是 long,因此不会产生额外的编码成本,因此您只剩下 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/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com