Why does NextValue call of performanceCounter change thread affinity mask

636 Views Asked by At

I have a C# project, where I have to both access the current workload of my processor, and ensure, that I run some specific code on every kernel of the processor. My problem is, that accessing the workload of my processor seems to prevent me from correctly assigning a thread affinity mask. I have some code here, that illustrates the problem:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace KernelAffinitySpike
{
    class Program
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);

        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern IntPtr GetCurrentThread();

        private static PerformanceCounter cpuUsage;
        private static UIntPtr oldMask, newMask, testMask; // thread-level processor affinity masks.

        static void Main(string[] args)
        {
            InitPerformanceCounter();

            Console.WriteLine("Pre: thread affinity: " + CurrentThreadAffinityMask());
            if (AllKernelsAccessible())
                Console.WriteLine("Pre: all kernels accessible");
            else
            {
                Console.Write("Pre: some kernels not accessible: ");
                foreach (UInt32 kernel in InaccessibleKernels())
                    Console.Write(kernel + " ");
                Console.WriteLine();
            }

            float load = cpuUsage.NextValue();

            Console.WriteLine("Post: thread affinity: " + CurrentThreadAffinityMask());
            if (AllKernelsAccessible())
                Console.WriteLine("Post: all kernels accessible");
            else
            {
                Console.Write("Post: some kernels not accessible: ");
                foreach (UInt32 kernel in InaccessibleKernels())
                    Console.Write(kernel + " ");
                Console.WriteLine();
            }

            Console.ReadLine();
        }

        static void InitPerformanceCounter()
        {
            cpuUsage = new PerformanceCounter();
            cpuUsage.CategoryName = "Processor";
            cpuUsage.CounterName = "% Processor Time"; 
            cpuUsage.InstanceName = "_Total";
        }

        static UInt32 CurrentThreadAffinityMask()
        {
            oldMask = SetThreadAffinityMask(GetCurrentThread(), (UIntPtr) 3); // 3 just enables all processors on a dual core. I'm only interested in the return value.
            SetThreadAffinityMask(GetCurrentThread(), oldMask);
            return (UInt32) oldMask;
        }

        static List<UInt32> InaccessibleKernels()
        {
            List<UInt32> inaccessible = new List<UInt32>();
            for (int i = 0; i < Environment.ProcessorCount; i++)
            {
                newMask = (UIntPtr)(1 << i);
                oldMask = SetThreadAffinityMask(GetCurrentThread(), newMask);
                testMask = SetThreadAffinityMask(GetCurrentThread(), oldMask);
                if (newMask != testMask)
                    inaccessible.Add((UInt32) newMask);
            }
            return inaccessible;
        }

        static bool AllKernelsAccessible()
        {
            return InaccessibleKernels().Count == 0;
        }
    }
}

Running this code yields the following output:

Pre: thread affinity: 3
Pre: all kernels accessible
Post: thread affinity: 2
Post: some kernels not accessible: 1 

So, it seems that the cpuUsage.NextValue call somehow changes the thread affinity mask, and also makes it impossible to change the mask to 1. It does make sense, that the Nextvalue call would have to interact with the thread affinity mask in some way, if it is aggregating a performance count from each kernel, but I cannot understand, why it should affect future changes to the thread affinity mask. Does anybody have an explanation, or a workaround to this problem?

1

There are 1 best solutions below

0
On BEST ANSWER

Here is some other guy with same problem

Seems like an unsolved issue from Microsoft.

Here is statement of the problem - Performance Counters change the affinity of the thread. Microsoft support.

They suggest calling to SetThreadAffinity. ) Obviously, their solution, is not working.