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). 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.
I'm running the above code on a number of threads - somewhere between 1 to 200 threads, depending on user input. The computers running this code have 2-16 cores - users use lower thread counts on the weaker machines.
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. So the code gets lTimestamp1, then another thread starts executing (and the current thread waits). Eventually th开发者_StackOverflowe current thread gets back control and retrieves 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. The tick count, however, increases anyway, so the duration is now really
Code block time = A + B + some time spent in other threads
while I want it to be only
Code block time = 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. 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.
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. Thanks for all the replies!
This can be done with the Native API call GetThreadTimes. Here is a article on CodeProject that uses it.
A second option is use 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. 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.
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?
精彩评论