Time measurement in processes

I need to measure the latency of the connection between two processes on the same machine. The best way I came across is serializing DateTime.UtcNow ( DateTime.Now seems so slow that it extremely distorts my measurements) into a message and comparing it with DateTime.UtcNow in another process. This is as good as it gets? Or is there a better way?

+4
source share
1 answer

If your goal is to measure and compare the exact time between processes, you should use the Windows API function QueryPerformanceCounter() . The return values ​​are synchronized between the processes as they return the value of the internal processor.

Stopwatch also uses QueryPerformanceCounter() in its implementation, but does not output the absolute values ​​that are returned, so you cannot use it.

You need to use P / Invoke to call QueryPerformanceCounter () , but it's pretty easy.

The overhead of using P / Invoke is small. From the MSDN documentation :

PInvoke has an overhead of 10 to 30 x86 instructions for each call. In addition to this fixed cost, marshaling creates additional overhead. There is no marching fee 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.

Since the value returned from QueryPerformanceCounter() is long, there will be no additional costs for marshaling, so you will be left with overhead for 10-30 instructions.

Also see this MSDN blog for UtcNow resolution of about 10 ms, which is quite large compared to the performance counter resolution. (Although I really don't think this is true for Windows 8, my measurements seem to show that UtcNow has a millisecond resolution).

In any case, it's easy to show that P / Invoking QueryPerformanceCounter () has a higher resolution than using DateTime.UtcNow.

If you run the build release of the following code (run from the OUTSIDE debugger), you will see that almost all the elapsed time of DateTime.UtcNow is 0, while all QueryPerformanceCounter () are non-zero.

This is because the DateTime.UtcNow resolution is not large enough to measure the elapsed time of the Thread.Sleep(0) call, while QueryPerformanceCounter() is.

 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); } } 

Now I understand what it may be that the overhead of calling QueryPerformanceCounter () is so high that it measures how long it takes to call, and not how long Thread.Sleep(0) . We can fix this in two ways:

First, we can change the first loop as follows:

 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); } 

Now in UtcNow should be specified Thread.Sleep (0) and two calls to QueryPerformanceCounter (). But if you run it, you will still see almost all past times equal to zero.

Secondly, we can specify the time required to call QueryPerformanceCounter () a million times:

 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); 

On my system, it takes about 32 ms to call QueryPerformanceCounter () one million times.

Finally, we can specify the time required to call DateTime.UtcNow a million times:

 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); 

On my system, which takes about 10 ms, which is about 3 times faster than calling QueryPerformanceCounter ().

In summary

So DateTime.UtcNow has less overhead than P / Invoking QueryPerformanceCounter (), but has a much lower resolution.

So, you pay your money, and you take your choice!

+2
source

All Articles