By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,247 Members | 1,273 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,247 IT Pros & Developers. It's quick & easy.

need help w/ multi-threaded, multi-CPU tick count / stopwatch

P: n/a
So I have a motherboard with multiple CPU sockets. It seems that if I
create a StopWatch on one thread and then call the Elapsed member from
a different thread that sometimes I get a tick count that's a million
miles away.

My thinking is that I can subclass the StopWatch. Then when the
Elapsed member is called, I can invoke it on the thread that the
StopWatch class was created on. True? How?

Or is there an easy way that I could scan through all processors and
read their tick counts? That way I could just have my own timer class
that stores counts for all CPUs.

Can the BeginThreadAffinity help me somehow? I'm totally at a loss as
to what that function does for me.

Is there some other solution?

I'm using .NET 2.0.

Thanks.

Feb 14 '07 #1
Share this Question
Share on Google+
5 Replies


P: n/a
On Feb 14, 1:44 pm, "not_a_commie" <notacom...@gmail.comwrote:
So I have a motherboard with multiple CPU sockets. It seems that if I
create a StopWatch on one thread and then call the Elapsed member from
a different thread that sometimes I get a tick count that's a million
miles away.

My thinking is that I can subclass the StopWatch. Then when the
Elapsed member is called, I can invoke it on the thread that the
StopWatch class was created on. True? How?

Or is there an easy way that I could scan through all processors and
read their tick counts? That way I could just have my own timer class
that stores counts for all CPUs.

Can the BeginThreadAffinity help me somehow? I'm totally at a loss as
to what that function does for me.

Is there some other solution?

I'm using .NET 2.0.

Thanks.
Hi,

The StopWatch class isn't inherently thread-safe. Are you
synchronizing access to it appropriately. Can you post some code
demonstrating the problem?

Brian

Feb 14 '07 #2

P: n/a
"not_a_commie" <no********@gmail.comwrote in message
news:11**********************@m58g2000cwm.googlegr oups.com...
So I have a motherboard with multiple CPU sockets. It seems that if I
create a StopWatch on one thread and then call the Elapsed member from
a different thread that sometimes I get a tick count that's a million
miles away.

My thinking is that I can subclass the StopWatch. Then when the
Elapsed member is called, I can invoke it on the thread that the
StopWatch class was created on. True? How?

Or is there an easy way that I could scan through all processors and
read their tick counts? That way I could just have my own timer class
that stores counts for all CPUs.

Can the BeginThreadAffinity help me somehow? I'm totally at a loss as
to what that function does for me.

Is there some other solution?

I'm using .NET 2.0.

Thanks.
Check this: http://support.microsoft.com/?id=896256

Willy.

Feb 14 '07 #3

P: n/a
The StopWatch class isn't inherently thread-safe. Are you
synchronizing access to it appropriately. Can you post some code
demonstrating the problem?
Right. The StopWatch class expects that the Reset/Start/Stop/Elapsed*
methods are all accessed from the same thread. Not only that, they all
have to be accessed from the same CPU. Does StopWatch use the
BeginThreadAffinity internally to make this happen? I'll assume that
it does. The problem is that if you need to call Reset in one thread
and Elapsed in another, this is very difficult. I did manage to make a
class to do it, however. Here it is for your critique:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace Blah
{
/// <summary>
/// Represents a high-resolution stopwatch that is thread-safe and
CPU safe.
/// </summary>
public sealed class StopWatch
{
static private Thread _backgroundThread = null;
static private EventWaitHandle _tickCountUpdateRequest = new
EventWaitHandle(false, EventResetMode.AutoReset);
static private EventWaitHandle _tickCountUpdateDone = new
EventWaitHandle(false, EventResetMode.AutoReset);

static public long Frequency = 0;
static public long CurrentTickCount = 0;

public long StartTickCount = 0;

/// <summary>
/// A thread to make sure CPU tick counts are always read from the
same CPU
/// </summary>
private static void _backgroundThreadFunc()
{
Thread.BeginThreadAffinity();
Frequency = System.Diagnostics.Stopwatch.Frequency;
while (true)
{
_tickCountUpdateRequest.WaitOne();
CurrentTickCount = System.Diagnostics.Stopwatch.GetTimestamp();
_tickCountUpdateDone.Set();
}
//Thread.EndThreadAffinity(); // never called
}

/// <summary>
/// The System.Diagnostics.Stopwatch (and hence, performance
counters) get their tick marks from the CPU that the thread is on. We
create a thread to make sure the tick counts are always read on the
same CPU.
/// </summary>
static StopWatch() {
_backgroundThread = new Thread(new
ThreadStart(_backgroundThreadFunc));
_backgroundThread.IsBackground = true;
_backgroundThread.Start();
}

/// <summary>
/// Initializes a new instance of the StopWatch class.
/// </summary>
/// <exception cref="NotSupportedException">The system does not have
a high-resolution performance counter.</exception>
public StopWatch()
{
Reset();
}

/// <summary>
/// Resets the stopwatch. This method should be called when you
start measuring.
/// </summary>
/// <exception cref="NotSupportedException">The system does not have
a high-resolution performance counter.</exception>
public void Reset()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
StartTickCount = CurrentTickCount;
}
}

/// <summary>
/// Peg the processor for this many seconds; mainly used for testing
/// </summary>
/// <param name="seconds"></param>
public static void BusyLoop(double seconds)
{
DateTime endTime = DateTime.Now.AddSeconds(seconds);
int i = 0;
while (DateTime.Now < endTime) { i++; }
}

public long GetCurrentTime_ms()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return CurrentTickCount * 1000 / StopWatch.Frequency;
}
}

/// <summary>
/// Get the time elapsed, in seconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapsed time in seconds.</returns>
public double GetElapsed_s()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return (double)(CurrentTickCount - StartTickCount) /
(double)StopWatch.Frequency;
}
}

/// <summary>
/// Get the time elapsed, in milliseconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapsed time in milliseconds.</returns>
public long GetElapsed_ms()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return (CurrentTickCount - StartTickCount) * 1000 /
StopWatch.Frequency;
}
}
}
}

Feb 14 '07 #4

P: n/a
"not_a_commie" <no********@gmail.comwrote in message
news:11*********************@l53g2000cwa.googlegro ups.com...
>The StopWatch class isn't inherently thread-safe. Are you
synchronizing access to it appropriately. Can you post some code
demonstrating the problem?

Right. The StopWatch class expects that the Reset/Start/Stop/Elapsed*
methods are all accessed from the same thread. Not only that, they all
have to be accessed from the same CPU. Does StopWatch use the
BeginThreadAffinity internally to make this happen? I'll assume that
it does. The problem is that if you need to call Reset in one thread
and Elapsed in another, this is very difficult. I did manage to make a
class to do it, however. Here it is for your critique:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading;

namespace Blah
{
/// <summary>
/// Represents a high-resolution stopwatch that is thread-safe and
CPU safe.
/// </summary>
public sealed class StopWatch
{
static private Thread _backgroundThread = null;
static private EventWaitHandle _tickCountUpdateRequest = new
EventWaitHandle(false, EventResetMode.AutoReset);
static private EventWaitHandle _tickCountUpdateDone = new
EventWaitHandle(false, EventResetMode.AutoReset);

static public long Frequency = 0;
static public long CurrentTickCount = 0;

public long StartTickCount = 0;

/// <summary>
/// A thread to make sure CPU tick counts are always read from the
same CPU
/// </summary>
private static void _backgroundThreadFunc()
{
Thread.BeginThreadAffinity();
Frequency = System.Diagnostics.Stopwatch.Frequency;
while (true)
{
_tickCountUpdateRequest.WaitOne();
CurrentTickCount = System.Diagnostics.Stopwatch.GetTimestamp();
_tickCountUpdateDone.Set();
}
//Thread.EndThreadAffinity(); // never called
}

/// <summary>
/// The System.Diagnostics.Stopwatch (and hence, performance
counters) get their tick marks from the CPU that the thread is on. We
create a thread to make sure the tick counts are always read on the
same CPU.
/// </summary>
static StopWatch() {
_backgroundThread = new Thread(new
ThreadStart(_backgroundThreadFunc));
_backgroundThread.IsBackground = true;
_backgroundThread.Start();
}

/// <summary>
/// Initializes a new instance of the StopWatch class.
/// </summary>
/// <exception cref="NotSupportedException">The system does not have
a high-resolution performance counter.</exception>
public StopWatch()
{
Reset();
}

/// <summary>
/// Resets the stopwatch. This method should be called when you
start measuring.
/// </summary>
/// <exception cref="NotSupportedException">The system does not have
a high-resolution performance counter.</exception>
public void Reset()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
StartTickCount = CurrentTickCount;
}
}

/// <summary>
/// Peg the processor for this many seconds; mainly used for testing
/// </summary>
/// <param name="seconds"></param>
public static void BusyLoop(double seconds)
{
DateTime endTime = DateTime.Now.AddSeconds(seconds);
int i = 0;
while (DateTime.Now < endTime) { i++; }
}

public long GetCurrentTime_ms()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return CurrentTickCount * 1000 / StopWatch.Frequency;
}
}

/// <summary>
/// Get the time elapsed, in seconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapsed time in seconds.</returns>
public double GetElapsed_s()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return (double)(CurrentTickCount - StartTickCount) /
(double)StopWatch.Frequency;
}
}

/// <summary>
/// Get the time elapsed, in milliseconds, since the last Reset() or
since
/// creation if Reset() hasn't been called since then.
/// </summary>
/// <returns>Elapsed time in milliseconds.</returns>
public long GetElapsed_ms()
{
lock (_backgroundThread)
{
StopWatch._tickCountUpdateRequest.Set();
StopWatch._tickCountUpdateDone.WaitOne();
return (CurrentTickCount - StartTickCount) * 1000 /
StopWatch.Frequency;
}
}
}
}

Please read my other reply, your issue is probably related to a failure of the SMP HAL to
synchronize the CPU clocks when running this on anything else than Vista or Longorn.

Willy.

Feb 14 '07 #5

P: n/a
On Feb 14, 3:34 pm, "not_a_commie" <notacom...@gmail.comwrote:
Right. The StopWatch class expects that the Reset/Start/Stop/Elapsed*
methods are all accessed from the same thread. Not only that, they all
have to be accessed from the same CPU. Does StopWatch use the
BeginThreadAffinity internally to make this happen? I'll assume that
it does. The problem is that if you need to call Reset in one thread
and Elapsed in another, this is very difficult. I did manage to make a
class to do it, however. Here it is for your critique:
Ah yes, there are processor affinity problems. I didn't notice this
note the first time I read through the documentation.

"On a multiprocessor computer, it does not matter which processor the
thread runs on. However, because of bugs in the BIOS or the Hardware
Abstraction Layer (HAL), you can get different timing results on
different processors. To specify processor affinity for a thread, use
the ProcessThread.ProcessorAffinity method."

I didn't see anything about thread affinity though so it should work
when called from different threads right?

Brian

Feb 14 '07 #6

This discussion thread is closed

Replies have been disabled for this discussion.