繁体   English   中英

测量整个过程的时间

[英]measuring time across processes

我需要测量同一台计算机上两个进程之间的通信延迟。我想出的最好方法是序列化DateTime.UtcNowDateTime.Now似乎太慢了,以至于它极大地扭曲了我的测量结果)到消息中并进行比较在另一个过程中将其与DateTime.UtcNow一起使用。 这是一样好吗? 或者,还有更好的方法?

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

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

您将必须使用P / Invoke来调用QueryPerformanceCounter(),但这非常简单。

使用P / Invoke的开销很小。 从MSDN文档

PInvoke每个调用的开销为10到30条x86指令。 除此固定成本外,封送处理还会产生额外的开销。 在托管和非托管代码中具有相同表示形式的可绑定表类型之间没有封送处理成本。 例如,在int和Int32之间进行转换没有成本。

由于从QueryPerformanceCounter()返回的值很长,因此不会产生额外的封送处理成本,因此您将剩下10到30条指令的开销。

另请参见此MSDN博客 ,其中指出UtcNow的分辨率约为10毫秒-与性能计数器的分辨率相比,这是非常大的。 (尽管我实际上不相信Windows 8确实如此;我的测量结果似乎表明UtcNow具有毫秒级的分辨率)。

无论如何,很容易证明与使用DateTime.UtcNow相比,P /调用QueryPerformanceCounter()具有更高的分辨率。

如果运行以下代码的发布版本(从调试器外部运行),您将看到几乎所有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 /调用QueryPerformanceCounter()的开销要低,但它的分辨率要低得多。

因此,您付钱,您就可以选择!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM