简体   繁体   English

精确测量线程中代码的执行时间(C#)

[英]Precisely measure execution time of code in thread (C#)

I'm trying to measure the execution time of some bits of code as accurately as possible on a number of threads, taking context switching and thread downtime into account. 我试图在一些线程上尽可能准确地测量一些代码的执行时间,同时考虑上下文切换和线程停机时间。 The application is implemented in C# (VS 2008). 该应用程序在C#(VS 2008)中实现。 Example: 例:

public void ThreadFunc ()
{
    // Some code here

    // Critical block #1 begins here
    long lTimestamp1 = Stopwatch.GetTimestamp ();

    CallComplex3rdPartyFunc (); // A

    long lTimestamp2 = Stopwatch.GetTimestamp ();
    // Critical block #1 ends here

    // Some code here

    // Critical block #2 begins here
    long lTimestamp3 = Stopwatch.GetTimestamp ();

    CallOtherComplex3rdPartyFunc (); // B

    long lTimestamp4 = Stopwatch.GetTimestamp ();
    // Critical block #2 ends here

    // Save timestamps for future analysis.
}

public int Main ( string[] sArgs )
{
    // Some code here

    int nCount = SomeFunc ();

    for ( int i = 0; i < nCount; i++ )
    {
        Thread oThread = new Thread ( ThreadFunc );
        oThread.Start ();
    }

    // Some code here

    return ( 0 );
}

I'd like to measure the execution time of the above two critical code blocks as accurately as possible. 我想尽可能准确地测量上述两个关键代码块的执行时间。 The two calls marked as A and B are potentially long function calls that may sometimes take several seconds to execute but in some cases they may complete in a few milliseconds. 标记为AB的两个调用可能是长函数调用,有时可能需要几秒钟才能执行,但在某些情况下,它们可能会在几毫秒内完成。

I'm running the above code on a number of threads - somewhere between 1 to 200 threads, depending on user input. 我在许多线程上运行上面的代码 - 介于1到200个线程之间,具体取决于用户输入。 The computers running this code have 2-16 cores - users use lower thread counts on the weaker machines. 运行此代码的计算机具有2-16个核心 - 用户在较弱的计算机上使用较低的线程数。

The problem is that A and B are both potenitally long functions so it's very likely that at least one context switch will happen during their execution - possibly more than one. 问题是AB都是潜在的长函数,因此很可能在执行期间至少会发生一次上下文切换 - 可能不止一次。 So the code gets lTimestamp1, then another thread starts executing (and the current thread waits). 所以代码获取lTimestamp1,然后另一个线程开始执行(当前线程等待)。 Eventually the current thread gets back control and retrieves lTimestamp2. 最终当前线程返回控制并检索lTimestamp2。

This means that the duration between lTimestamp1 and lTimestamp2 includes time when the thread was not actually running - it was waiting to be scheduled again while other threads executed. 这意味着lTimestamp1lTimestamp2之间的持续时间包括线程实际上没有运行的时间 - 它正在等待在执行其他线程时再次调度。 The tick count, however, increases anyway, so the duration is now really 但是,滴答计数无论如何都会增加,所以持续时间现在确实如此

Code block time = A + B + some time spent in other threads 代码块时间= A + B + 在其他线程中花费的时间

while I want it to be only 虽然我希望它只是

Code block time = A + B 代码块时间= A + B.

This is especially an issue with a larger number of threads, since they'll all get a chance to run, so the above timings will be higher while all other threads run before the thread in question gets another chance to run. 这对于大量线程尤其是一个问题,因为它们都有机会运行,因此上述时间将更高,而所有其他线程在相关线程运行之前运行。

So my question is: is it possible to somehow calculate the time when the thread is not running and then adjust the above timings accordingly? 所以我的问题是:是否有可能以某种方式计算线程运行的时间,然后相应地调整上述时间? I'd like to eliminate (subtract) that 3rd term entirely or at least as much of it as possible. 我想完全或至少尽可能地消除(减去)第三个词。 The code runs millions of times, so final timings are calculated from a lot of samples and then averaged out. 代码运行数百万次,因此最终时间是从大量样本计算出来的,然后取平均值。

I'm not looking for profiler products, etc. - the application needs to time these the marked parts as accurately as possible. 我不是在寻找探查器产品等 - 应用程序需要尽可能准确地计算这些标记的部件。 The functions A and B are 3rd party functions, I cannot change them in any way. 功能AB是第三方功能,我无法以任何方式更改它们。 I'm also aware of the possible fluctuations when measuring time with nanosecond precision and possible overhead inside those 3rd-party functions, but I still need to do this measurement. 我也知道在测量纳秒级精度的时间和第三方功能内部可能的开销时可能存在的波动,但我仍然需要进行这种测量。

Any advice would be greatly appreciated - C++ or x86 assembly code would work as well. 任何建议都将不胜感激 - C ++或x86汇编代码也可以。

Edit: seems to be impossible to implement this. 编辑:似乎无法实现这一点。 Scott's idea below (using GetThreadTimes) is good but unfortunately GetThreadTimes() is a flawed API and it almost never returns correct data. 斯科特的想法如下(使用GetThreadTimes)很好但不幸的是GetThreadTimes()是一个有缺陷的API,它几乎永远不会返回正确的数据。 Thanks for all the replies! 感谢所有的答复!

This can be done with the Native API call GetThreadTimes . 这可以使用Native API调用GetThreadTimes来完成。 Here is a article on CodeProject that uses it. 这是一篇关于使用它的CodeProject的文章。

A second option is use QueryThreadCycleTime . 第二个选项是使用QueryThreadCycleTime This will not give you the time, but it will give you the number of cycles the current thread has been executing. 这不会给你时间,但它会给你当前线程执行的周期数。

Be aware you can't just directly convert cycles->seconds due to the fact that many processors (especially mobile processors) do not run at a fixed speed so there is no constant number you could multiply by to get the elapsed time in seconds. 请注意,由于许多处理器(尤其是移动处理器)不以固定速度运行,所以不能直接转换cycles->seconds ,因此没有常数可以乘以得到经过的时间(以秒为单位)。 But if you are using a processor that does not vary its speed it then would be a simple math problem to get wall clock time from the cycles. 但是,如果您使用的处理器不会改变其速度,那么从周期中获取挂钟时间将是一个简单的数学问题。

You can use Stopwatch.Start() and Stopwatch.Stop() methods to pause/continue time measurement, it does not reset Elapsed / ElapsedMilliseconds value so perhaps you can leverage this. 你可以使用Stopwatch.Start()Stopwatch.Stop()方法暂停/继续时间测量,它不会重置Elapsed / ElapsedMilliseconds值,所以也许你可以利用它。

Regarding thread context switches - I believe there are no ways to handle it in managed code so this is not possible to exclude time when thread was suspended 关于线程上下文切换 - 我相信没有办法在托管代码中处理它,所以这不可能排除线程被挂起时的时间

EDIT: 编辑:

An interesting article with benchmarks: How long does it take to make a context switch? 一篇有趣的文章与基准测试: 上下文切换需要多长时间?

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

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