Solved

# Using QueryPerformanceCounter on a multi-core system

Posted on 2011-09-12
Medium Priority
1,349 Views
I need to use the Win API QueryPerformanceCounter call in my VB.NET 2008 application.  The documentation I can find warns that the call result can be unreliable on multicore systems unless SetThreadAffinityMask is called to force the QueryPerformanceCounter call to always run on the same core.  I'm having trouble figuring out exactly how to use GetProcessAffinityMask and SetThreadAffinityMask to accomplish this.

Can anybody offer any known-good code examples?
0
Question by:RHenningsgard
• 4
• 3

LVL 96

Expert Comment

ID: 36528858
I am wondering what special functionality that you need that you can't get from the CLR performance counter stuff?

Getting Started With the Performance Counters
http://www.getdotnetcode.com/gdncstore/free/Articles/Getting%20Started%20With%20Performance%20Counters.htm
0

LVL 2

Author Comment

ID: 36529407
Thanks for the interesting link, but I didn't find anything relevant to my issue there.

I am using Kernel32.DLL's QueryPerformanceFrequency() call to get the frequency at which the counter updates, and QueryPerformanceCounter() to get the time count in ticks at the frequency reported by QueryPerformanceFrequency().  The application is a precision measurement system which continuously logs a device's outputs, and the current time.  It's kind of like a "flight data recorder" operation.  So my need is to repeatedly get the time, at the highest accuracy and precision possible, so my log results won't jitter due to errors in the reported time.

If it were not for the multicore OS environment and the stern warnings of Microsoft, I'd be using assembly and the RDTSC "Read Time Stamp Counter" instruction to just get the accumulated CPU ticks and compute the time from that.  The warnings are that in a multicore system, you can never be certain which CPU's count you're receiving, and therefore it's possible to get wild swings in the reported "time."

A similar warning appears in several places in the MSDN documentation about the stability of the QueryPerformanceCounter output, and the recommendation is the use of GetProcessAffinityMask and then SetThreadAffinityMask to force the calls to always occur in the same core.  That's why I'm looking for known-good sample code, as I've had difficulty figuring out how to code the VB.NET to get those calls to work.
0

LVL 96

Expert Comment

ID: 36529418
I was asking why you are using Win32 API, instead of the managed performance counter?
0

LVL 2

Author Comment

ID: 36529532
I have been unable to figure out how to get the managed performance counter to just provide me the system time in high-resolution ticks, the way the unmanaged QueryPerformanceCounter() does.  It seems that all of the calls I can find in the managed PerformanceCounter class pertain to measuring OS and process performance, not just giving me the raw time.
0

LVL 96

Accepted Solution

Bob Learned earned 2000 total points
ID: 36530270
That is good information to know.

I would take a look here:

Spike Your CPU’s Processor in .Net
http://omegacoder.com/?p=94

Calculate PI
http://omegacoder.com/?p=91
using System;
using System.Runtime.InteropServices;
using System.Text;

// Target a specific processor for the thread to run on
{

[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentThread();

[DllImport("kernel32.dll")]

public static void Usage()
{
int cpu = 0;
Console.WriteLine("Spike CPU 1");
tp.SpikeCPU(cpu);
if (tp._ex != null)
{
Console.WriteLine(tp._ex.Message);
}
else
{
if (Environment.ProcessorCount > 1)
{
while (++cpu < Environment.ProcessorCount)
{
Console.WriteLine("Spike CPU " + (cpu + 1).ToString());
tp.SpikeCPU(cpu);
if (tp._ex != null)
{
Console.WriteLine(tp._ex.Message);
break;
}
}
}
else // Either a single CPU or hyperthreading not enabled in the OS or the BIOS.
{
Console.WriteLine("This PC does not have two processors available.");
}
}
}
private const int PiSignificantDigits = 750; // Adjust to your processor
// Spike the CPU and waith for it to finish
public void SpikeCPU(int targetCPU)
{
// Create a worker thread for the work.
_worker = new Thread(DoBusyWork);         // Background is set so not to not prevent the
// mainprocess from terminating if someone closes it.
_worker.IsBackground = true;
_worker.Start((object) targetCPU);
_worker.Join(); // Wait for it to be done.
}

public void DoBusyWork(object target)
{
try
{
int processor = (int) target;
if (Environment.ProcessorCount > 1)
{
}
CalculatePI.Process(PiSignificantDigits);
}
catch (Exception ex)
{
_ex = ex;
}
}
public Exception _ex = null;
}

// Source:
// http://omegacoder.com/?p=91

public class CalculatePI
{
/*
Computation of the n'th decimal digit of \pi with very little memory.
Written by Fabrice Bellard on January 8, 1997.
We use a slightly modified version of the method described by Simon
Plouffe in "On the Computation of the n'th decimal digit of various
transcendental numbers" (November 1996). We have modified the algorithm
to get a running time of O(n^2) instead of O(n^3log(n)^3).

This program uses mostly integer arithmetic. It may be slow on some
hardware where integer multiplications and divisions must be done
by software. We have supposed that 'int' has a size of 32 bits. If
your compiler supports 'long long' integers of 64 bits, you may use
the integer version of 'mul_mod' (see HAS_LONG_LONG).

Call this static to use.
*/

public static string Process(int digits)
{
StringBuilder result = new StringBuilder();
result.Append("3.");
DateTime StartTime = DateTime.Now;
if (digits > 0)
{
for (int i = 0; i < digits; i += 9)
{
String ds = CalculatePiDigits(i + 1);
int digitCount = Math.Min(digits - i, 9);
if (ds.Length < 9)
ds = string.Format("{0:D9}", int.Parse(ds));
result.Append(ds.Substring(0, digitCount));
}
}
return result.ToString();
}
private static int mul_mod(int a, int b, int m)
{
return (int) (((long) a * (long) b) % m);
}
/* return the inverse of x mod y */
private static int inv_mod(int x, int y)
{
int q, u, v, a, c, t;
u = x;
v = y;
c = 1;
a = 0;
do
{
q = v / u;
t = c;
c = a - q * c;
a = t;
t = u;
u = v - q * u;
v = t;
} while (u != 0);

a = a % y;
if (a < 0)
{
a = y + a;
}
return a;
}
/* return (a^b) mod m */

private static int pow_mod(int a, int b, int m)
{
int r, aa;
r = 1;
aa = a;
while (true)
{
if ((b & 1) != 0)
{
r = mul_mod(r, aa, m);
}

b = b >> 1;

if (b == 0)
{
break;
}

aa = mul_mod(aa, aa, m);
}
return r;
}

/* return true if n is prime */
private static bool is_prime(int n)
{
if ((n % 2) == 0)
{
return false;
}

int r = (int) Math.Sqrt(n);

for (int i = 3; i <= r; i += 2)
{
if ((n % i) == 0)
{
return false;
}
}
return true;
}

/* return the prime number immediatly after n */
private static int next_prime(int n)
{
do
{
n++;
} while (!is_prime(n));
return n;
}

private static String CalculatePiDigits(int n)
{
int av, vmax, num, den, s, t;
int N = (int) ((n + 20) * Math.Log(10) / Math.Log(2));
double sum = 0;
for (int a = 3; a <= (2 * N); a = next_prime(a))
{
vmax = (int) (Math.Log(2 * N) / Math.Log(a));
av = 1;
for (int i = 0; i < vmax; i++)
{
av = av * a;
}
s = 0;
num = 1;
den = 1;
int v = 0;
int kq = 1;
int kq2 = 1;

for (int k = 1; k <= N; k++)
{
t = k;
if (kq >= a)
{
do
{
t = t / a;
v--;
} while ((t % a) == 0);

kq = 0;
}
kq++;
num = mul_mod(num, t, av);
t = 2 * k - 1;
if (kq2 >= a)
{
if (kq2 == a)
{
do
{
t = t / a;
v++;
} while ((t % a) == 0);
}

kq2 -= a;
}
den = mul_mod(den, t, av);
kq2 += 2;
if (v > 0)
{
t = inv_mod(den, av);
t = mul_mod(t, num, av);
t = mul_mod(t, k, av);
for (int i = v; i < vmax; i++)
{
t = mul_mod(t, a, av);
}
s += t;
if (s >= av)
{
s -= av;
}
}
}

t = pow_mod(10, n - 1, av);
s = mul_mod(s, t, av);
sum = (sum + (double) s / (double) av) % 1.0;
}
int Result = (int) (sum * 1e9);

String StringResult = String.Format("{0:D9}", Result);
return StringResult;
}

// Put a space between every group of 10 digits.
private static String breakDigitsIntoGroupsOf10(String digits)
{
String result = "";
while (digits.Length > 10)
{
result += digits.Substring(0, 10) + " ";
digits = digits.Substring(10, digits.Length - 10);
}
result += digits;
return result;
}
}

Imports System.Runtime.InteropServices
Imports System.Text

' Target a specific processor for the thread to run on

<DllImport("kernel32.dll")> _
Private Shared Function GetCurrentThread() As IntPtr
End Function

<DllImport("kernel32.dll")> _
End Function

Public Shared Sub Usage()
Dim cpu As Integer = 0
Dim tp As New ThreadProcessor()
Console.WriteLine("Spike CPU 1")
tp.SpikeCPU(cpu)
If tp._ex IsNot Nothing Then
Console.WriteLine(tp._ex.Message)
Else
If Environment.ProcessorCount > 1 Then
While System.Threading.Interlocked.Increment(cpu) < Environment.ProcessorCount
Console.WriteLine("Spike CPU " & (cpu + 1).ToString())
tp.SpikeCPU(cpu)
If tp._ex IsNot Nothing Then
Console.WriteLine(tp._ex.Message)
Exit While
End If
End While
Else
' Either a single CPU or hyperthreading not enabled in the OS or the BIOS.
Console.WriteLine("This PC does not have two processors available.")
End If
End If
End Sub

Private _worker As Thread
Private Const PiSignificantDigits As Integer = 750

' Spike the CPU and waith for it to finish
Public Sub SpikeCPU(targetCPU As Integer)
' Create a worker thread for the work.
' Background is set so not to not prevent the
' mainprocess from terminating if someone closes it.
_worker.IsBackground = True
_worker.Start(DirectCast(targetCPU, Object))
_worker.Join()
' Wait for it to be done.
End Sub

Public Sub DoBusyWork(target As Object)
Try
Dim processor As Integer = CInt(target)
If Environment.ProcessorCount > 1 Then
End If
CalculatePI.Process(PiSignificantDigits)
Catch ex As Exception
_ex = ex
End Try
End Sub

Public _ex As Exception = Nothing

End Class

' Source:
' http://omegacoder.com/?p=91

Public Class CalculatePI

'     Computation of the n'th decimal digit of \pi with very little memory.
'     Written by Fabrice Bellard on January 8, 1997.
'     We use a slightly modified version of the method described by Simon
'     Plouffe in "On the Computation of the n'th decimal digit of various
'     transcendental numbers" (November 1996). We have modified the algorithm
'     to get a running time of O(n^2) instead of O(n^3log(n)^3).
'
'     This program uses mostly integer arithmetic. It may be slow on some
'     hardware where integer multiplications and divisions must be done
'     by software. We have supposed that 'int' has a size of 32 bits. If
'     your compiler supports 'long long' integers of 64 bits, you may use
'     the integer version of 'mul_mod' (see HAS_LONG_LONG).
'
'     Call this static to use.

Public Shared Function Process(digits As Integer) As String
Dim result As New StringBuilder()
result.Append("3.")
Dim StartTime As DateTime = DateTime.Now
If digits > 0 Then
For i As Integer = 0 To digits - 1 Step 9
Dim ds As String = CalculatePiDigits(i + 1)
Dim digitCount As Integer = Math.Min(digits - i, 9)
If ds.Length < 9 Then
ds = String.Format("{0:D9}", Integer.Parse(ds))
End If
result.Append(ds.Substring(0, digitCount))
Next
End If
Return result.ToString()
End Function

Private Shared Function mul_mod(a As Integer, b As Integer, m As Integer) As Integer
Return CInt((CLng(a) * CLng(b)) Mod m)
End Function
' return the inverse of x mod y

Private Shared Function inv_mod(x As Integer, y As Integer) As Integer
Dim q As Integer, u As Integer, v As Integer, a As Integer, c As Integer, t As Integer
u = x
v = y
c = 1
a = 0
Do
q = v \ u
t = c
c = a - q * c
a = t
t = u
u = v - q * u
v = t
Loop While u <> 0

a = a Mod y
If a < 0 Then
a = y + a
End If
Return a
End Function
' return (a^b) mod m

Private Shared Function pow_mod(a As Integer, b As Integer, m As Integer) As Integer
Dim r As Integer, aa As Integer
r = 1
aa = a
While True
If (b And 1) <> 0 Then
r = mul_mod(r, aa, m)
End If

b = b >> 1

If b = 0 Then
Exit While
End If

aa = mul_mod(aa, aa, m)
End While
Return r
End Function

' return true if n is prime

Private Shared Function is_prime(n As Integer) As Boolean
If (n Mod 2) = 0 Then
Return False
End If

Dim r As Integer = CInt(Math.Truncate(Math.Sqrt(n)))

For i As Integer = 3 To r Step 2
If (n Mod i) = 0 Then
Return False
End If
Next
Return True
End Function

' return the prime number immediatly after n

Private Shared Function next_prime(n As Integer) As Integer
Do
n += 1
Loop While Not is_prime(n)
Return n
End Function

Private Shared Function CalculatePiDigits(n__1 As Integer) As String
Dim av As Integer, vmax As Integer, num As Integer, den As Integer, s As Integer, t As Integer
Dim N__2 As Integer = CInt(Math.Truncate((n__1 + 20) * Math.Log(10) / Math.Log(2)))
Dim sum As Double = 0
Dim a As Integer = 3
While a <= (2 * N__2)
vmax = CInt(Math.Truncate(Math.Log(2 * N__2) / Math.Log(a)))
av = 1
For i As Integer = 0 To vmax - 1
av = av * a
Next
s = 0
num = 1
den = 1
Dim v As Integer = 0
Dim kq As Integer = 1
Dim kq2 As Integer = 1

For k As Integer = 1 To N__2
t = k
If kq >= a Then
Do
t = t \ a
v -= 1
Loop While (t Mod a) = 0

kq = 0
End If
kq += 1
num = mul_mod(num, t, av)
t = 2 * k - 1
If kq2 >= a Then
If kq2 = a Then
Do
t = t \ a
v += 1
Loop While (t Mod a) = 0
End If

kq2 -= a
End If
den = mul_mod(den, t, av)
kq2 += 2
If v > 0 Then
t = inv_mod(den, av)
t = mul_mod(t, num, av)
t = mul_mod(t, k, av)
For i As Integer = v To vmax - 1
t = mul_mod(t, a, av)
Next
s += t
If s >= av Then
s -= av
End If
End If
Next

t = pow_mod(10, n__1 - 1, av)
s = mul_mod(s, t, av)
sum = (sum + CDbl(s) / CDbl(av)) Mod 1.0
a = next_prime(a)
End While
Dim Result As Integer = CInt(Math.Truncate(sum * 1000000000.0))

Dim StringResult As String = String.Format("{0:D9}", Result)
Return StringResult
End Function

' Put a space between every group of 10 digits.
Private Shared Function breakDigitsIntoGroupsOf10(digits As String) As String
Dim result As String = ""
While digits.Length > 10
result += digits.Substring(0, 10) & " "
digits = digits.Substring(10, digits.Length - 10)
End While
result += digits
Return result
End Function

End Class

0

LVL 2

Author Comment

ID: 36530689
This looks like the soul of a home run, but it will take me a day or two to extract what I need and re-code it into a usable form.  That said, I'm betting you get the "accepted solution" points when I'm done.

Thanks,

Rob---
0

LVL 2

Author Closing Comment

ID: 36537726
Nice dig!  That was exactly the information I needed to address the chore of persuading WinXP to run my thread on a specific core.

Thanks,

Rob---
0

## Featured Post

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

## Join & Write a Comment Already a member? Login.

A while ago, I was working on a Windows Forms application and I needed a special label control with reflection (glass) effect to show some titles in a stylish way. I've always enjoyed working with graphics, but it's never too clever to re-invent …
Creating an analog clock UserControl seems fairly straight forward.  It is, after all, essentially just a circle with several lines in it!  Two common approaches for rendering an analog clock typically involve either manually calculating points with…
Watch the video to know how one can repair corrupt Exchange OST file effortlessly and convert OST emails to MS Outlook PST file format by using Kernel for OST to PST converter tool. It can convert OST to MSG, MBOX, EML to access them. It can migrate…
Watch the video to know the simple way to remove or recover or reset lost or forgotten passwords of Outlook PST file. With Kernel Outlook Password Recovery tool such operation is very easy to perform. It is a freeware with limitation to use with 500…
###### Suggested Courses
Course of the Month6 days, 9 hours left to enroll

#### 589 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.