355

Is there a way via .NET/C# to find out the number of CPU cores?

PS This is a straight code question, not a "Should I use multi-threading?" question! :-)

carefulnow1
  • 803
  • 12
  • 30
MrGreggles
  • 6,113
  • 9
  • 42
  • 48
  • 8
    Do you need to know how many cores there are or how many logical processors there are? For just running multiple threads, either is probably sufficient, but there are scenarios where the difference could be important. – Kevin Kibler Apr 19 '10 at 20:27
  • Is there a newer way to do this? – MoonKnight Jan 02 '20 at 10:48

11 Answers11

518

There are several different pieces of information relating to processors that you could get:

  1. Number of physical processors
  2. Number of cores
  3. Number of logical processors.

These can all be different; in the case of a machine with 2 dual-core hyper-threading-enabled processors, there are 2 physical processors, 4 cores, and 8 logical processors.

The number of logical processors is available through the Environment class, but the other information is only available through WMI (and you may have to install some hotfixes or service packs to get it on some systems):

Make sure to add a reference in your project to System.Management.dll In .NET Core, this is available (for Windows only) as a NuGet package.

Physical Processors:

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
    Console.WriteLine("Number Of Physical Processors: {0} ", item["NumberOfProcessors"]);
}

Cores:

int coreCount = 0;
foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())
{
    coreCount += int.Parse(item["NumberOfCores"].ToString());
}
Console.WriteLine("Number Of Cores: {0}", coreCount);

Logical Processors:

Console.WriteLine("Number Of Logical Processors: {0}", Environment.ProcessorCount);

OR

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_ComputerSystem").Get())
{
    Console.WriteLine("Number Of Logical Processors: {0}", item["NumberOfLogicalProcessors"]);
}

Processors excluded from Windows:

You can also use Windows API calls in setupapi.dll to discover processors that have been excluded from Windows (e.g. through boot settings) and aren't detectable using the above means. The code below gives the total number of logical processors (I haven't been able to figure out how to differentiate physical from logical processors) that exist, including those that have been excluded from Windows:

static void Main(string[] args)
{
    int deviceCount = 0;
    IntPtr deviceList = IntPtr.Zero;
    // GUID for processor classid
    Guid processorGuid = new Guid("{50127dc3-0f36-415e-a6cc-4cb3be910b65}");

    try
    {
        // get a list of all processor devices
        deviceList = SetupDiGetClassDevs(ref processorGuid, "ACPI", IntPtr.Zero, (int)DIGCF.PRESENT);
        // attempt to process each item in the list
        for (int deviceNumber = 0; ; deviceNumber++)
        {
            SP_DEVINFO_DATA deviceInfo = new SP_DEVINFO_DATA();
            deviceInfo.cbSize = Marshal.SizeOf(deviceInfo);

            // attempt to read the device info from the list, if this fails, we're at the end of the list
            if (!SetupDiEnumDeviceInfo(deviceList, deviceNumber, ref deviceInfo))
            {
                deviceCount = deviceNumber;
                break;
            }
        }
    }
    finally
    {
        if (deviceList != IntPtr.Zero) { SetupDiDestroyDeviceInfoList(deviceList); }
    }
    Console.WriteLine("Number of cores: {0}", deviceCount);
}

[DllImport("setupapi.dll", SetLastError = true)]
private static extern IntPtr SetupDiGetClassDevs(ref Guid ClassGuid,
    [MarshalAs(UnmanagedType.LPStr)]String enumerator,
    IntPtr hwndParent,
    Int32 Flags);

[DllImport("setupapi.dll", SetLastError = true)]
private static extern Int32 SetupDiDestroyDeviceInfoList(IntPtr DeviceInfoSet);

[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
    Int32 MemberIndex,
    ref SP_DEVINFO_DATA DeviceInterfaceData);

[StructLayout(LayoutKind.Sequential)]
private struct SP_DEVINFO_DATA
{
    public int cbSize;
    public Guid ClassGuid;
    public uint DevInst;
    public IntPtr Reserved;
}

private enum DIGCF
{
    DEFAULT = 0x1,
    PRESENT = 0x2,
    ALLCLASSES = 0x4,
    PROFILE = 0x8,
    DEVICEINTERFACE = 0x10,
}
LionAM
  • 1,271
  • 10
  • 28
Kevin Kibler
  • 13,357
  • 8
  • 38
  • 61
  • 5
    WMI Code Creator will help with value discovery and query creation (it can even generate stubs in c#/vb.net). – StingyJack Apr 20 '10 at 12:53
  • I can't find ManagementObjectSearcher in System.Managment. – user2381422 Aug 06 '13 at 08:09
  • 4
    It's in System.Management.dll. Did you you include a reference to that assembly in your project? – Kevin Kibler Aug 12 '13 at 13:45
  • 3
    Minor off-by-one issue in the above code. Since `deviceCount` is zero-based, the core count should be output like this: `Console.WriteLine("Number of cores: {0}", deviceCount + 1);` – Francis Litterio Sep 09 '13 at 18:09
  • So if a machine has 2 physical processors each with 2 physical cores each of which has 2 logical cores, would `coreCount += int.Parse(item["NumberOfCores"].ToString());` give me coreCount of 4 and `item["NumberOfLogicalProcessors"]` give a count of 8? – PhoenixDev Jul 01 '15 at 11:51
  • 4
    Aren't you causing issues by not disposing the management objects and searchers? – Benjamin Dec 16 '15 at 19:27
  • 1
    My system is Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz, 3401 Mhz, 4 Core(s), 8 Logical Processor(s). The code above returns `7`... – Vlad Jul 09 '18 at 22:57
  • For those that don't like having to build query strings, you can [pass an `ObjectQuery` to the `ManagementObjectSearcher` constructor](https://docs.microsoft.com/dotnet/api/system.management.managementobjectsearcher.-ctor#System_Management_ManagementObjectSearcher__ctor_System_Management_ObjectQuery_) (e.g. `new ManagementObjectSearcher(new SelectQuery("Win32_ComputerSystem"))` or `new ManagementObjectSearcher(new SelectQuery("Win32_ComputerSystem", null, new string[] { "NumberOfProcessors" }))`). You'll still have to look up and pass the class and properties names from somewhere, of course. – Lance U. Matthews Oct 31 '19 at 19:01
  • PhoenixDev: deviceCount is incorrect, not coreCount – dynamichael Dec 12 '22 at 15:11
  • This is the single answer I found after hours that takes into account non-Windows cores, thanks! – piertoni Dec 13 '22 at 15:25
  • About **Logical Processors** ... note that 'Environment.ProcessorCount' returns number of cores available to **the current process**. If you have changed the affinity of the process, the value will be lower than the "real" number of available logical processors in the system. – Engin Mar 07 '23 at 11:53
233
Environment.ProcessorCount

[Documentation]

Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
  • 18
    That's so beautifully simple I'm almost shedding tears. Thx for the reply! – MrGreggles Oct 09 '09 at 10:43
  • 80
    This gives the number of logical processors, not the number of cores. – Kevin Kibler Apr 19 '10 at 20:23
  • 10
    @KevinKibler From the question, I suspect the OP doesn't understand the difference, and if you don't know the difference this is probably what you want. – Glenn Maynard Jun 15 '14 at 15:45
  • 1
    This also returns the wrong count on many core systems. I'm running two dodeca core processors with hyper-threading, which gives me a total of 48 logical processors. `Environment.ProcessorCount` yields 32. – Allen Clark Copeland Jr Aug 30 '15 at 02:53
  • 1
    @AlexanderMorou, yes this will fail to provide accurate results on some multi CPU servers. There is a fix for this, but havent tested it yet. – TheLegendaryCopyCoder Jun 23 '17 at 09:43
  • 1
    This returns the logical processors available to the current process. If you use affinity, the result may differ! – Engin Mar 07 '23 at 11:49
38

WMI queries are slow, so try to Select only the desired members instead of using Select *.

The following query takes 3.4s:

foreach (var item in new System.Management.ManagementObjectSearcher("Select * from Win32_Processor").Get())

While this one takes 0.122s:

foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())
Aleix Mercader
  • 481
  • 4
  • 9
  • 1
    What system are you running this on? I use multiple "Select *" queries and it doesn't take _anywhere_ near 3.4 seconds, tested on thousands of computers that my software is deployed on. I do a Select * because I am getting multiple properties from the object. However, I do it a bit different: create an ObjectQuery on the Select *; get the ManagementObjectCollection; then foreach ManagementObject in the ManagementObjectCollection. – deegee Aug 11 '13 at 20:23
  • @deegee: you are right, the query itself does not take much longer with "Select *", it's just that the int parsing below is slow if iterating all the values returned instead of just NumberOfCores. – Aleix Mercader Sep 13 '13 at 11:01
19

Environment.ProcessorCount should give you the number of cores on the local machine.

Mithrax
  • 7,603
  • 18
  • 55
  • 60
10

The the easyest way = Environment.ProcessorCount
Exemple from Environment.ProcessorCount Property

using System;

class Sample 
{
    public static void Main() 
    {
        Console.WriteLine("The number of processors " +
            "on this computer is {0}.", 
            Environment.ProcessorCount);
    }
}
Wang Liang
  • 4,244
  • 6
  • 22
  • 45
  • Method Environment.ProcessorCount sometimes shows incorrect data (see https://stackoverflow.com/questions/27965962/c-sharp-environment-processorcount-does-not-always-return-the-full-number-of-log) – constructor Nov 18 '19 at 10:19
  • Three other answers, including the [accepted one](https://stackoverflow.com/a/2670568/150605), already suggest using `Environment.ProcessorCount`. – Lance U. Matthews Aug 03 '22 at 19:04
9

It's rather interesting to see how .NET get this internally to say the least... It's as "simple" as below:

namespace System.Threading
{
    using System;
    using System.Runtime.CompilerServices;

    internal static class PlatformHelper
    {
        private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 0x7530;
        private static volatile int s_lastProcessorCountRefreshTicks;
        private static volatile int s_processorCount;

        internal static bool IsSingleProcessor
        {
            get
            {
                return (ProcessorCount == 1);
            }
        }

        internal static int ProcessorCount
        {
            get
            {
                int tickCount = Environment.TickCount;
                int num2 = s_processorCount;
                if ((num2 == 0) || ((tickCount - s_lastProcessorCountRefreshTicks) >= 0x7530))
                {
                    s_processorCount = num2 = Environment.ProcessorCount;
                    s_lastProcessorCountRefreshTicks = tickCount;
                }
                return num2;
            }
        }
    }
}
Ostati
  • 4,623
  • 3
  • 44
  • 48
  • 1
    Is this all that interesting, though? It's just caching the value from `Environment.ProcessorCount` for a period of time, and is only used `internal`ly by some synchronization-related classes. How `Environment.ProcessorCount` gets _its_ value is what would be informative. Also, it looks like you got this code from a decompiler instead of just from [the source](https://referencesource.microsoft.com/#mscorlib/system/threading/SpinWait.cs,a7801aeb755e9d6f). – Lance U. Matthews Aug 03 '22 at 19:02
4

From .NET Framework source

You can also get it with PInvoke on Kernel32.dll

The following code is coming more or less from SystemInfo.cs from System.Web source located here:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct SYSTEM_INFO
{
  public ushort wProcessorArchitecture;
  public ushort wReserved;
  public uint dwPageSize;
  public IntPtr lpMinimumApplicationAddress;
  public IntPtr lpMaximumApplicationAddress;
  public IntPtr dwActiveProcessorMask;
  public uint dwNumberOfProcessors;
  public uint dwProcessorType;
  public uint dwAllocationGranularity;
  public ushort wProcessorLevel;
  public ushort wProcessorRevision;
}

internal static class SystemInfo 
{
    static int _trueNumberOfProcessors;
    internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);    

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    internal static extern void GetSystemInfo(out SYSTEM_INFO si);

    [DllImport("kernel32.dll")]
    internal static extern int GetProcessAffinityMask(IntPtr handle, out IntPtr processAffinityMask, out IntPtr systemAffinityMask);

    internal static int GetNumProcessCPUs()
    {
      if (SystemInfo._trueNumberOfProcessors == 0)
      {
        SYSTEM_INFO si;
        GetSystemInfo(out si);
        if ((int) si.dwNumberOfProcessors == 1)
        {
          SystemInfo._trueNumberOfProcessors = 1;
        }
        else
        {
          IntPtr processAffinityMask;
          IntPtr systemAffinityMask;
          if (GetProcessAffinityMask(INVALID_HANDLE_VALUE, out processAffinityMask, out systemAffinityMask) == 0)
          {
            SystemInfo._trueNumberOfProcessors = 1;
          }
          else
          {
            int num1 = 0;
            if (IntPtr.Size == 4)
            {
              uint num2 = (uint) (int) processAffinityMask;
              while ((int) num2 != 0)
              {
                if (((int) num2 & 1) == 1)
                  ++num1;
                num2 >>= 1;
              }
            }
            else
            {
              ulong num2 = (ulong) (long) processAffinityMask;
              while ((long) num2 != 0L)
              {
                if (((long) num2 & 1L) == 1L)
                  ++num1;
                num2 >>= 1;
              }
            }
            SystemInfo._trueNumberOfProcessors = num1;
          }
        }
      }
      return SystemInfo._trueNumberOfProcessors;
    }
}
Bob Bryan
  • 3,687
  • 1
  • 32
  • 45
Fab
  • 14,327
  • 5
  • 49
  • 68
  • 3
    Tried this, but it returns the number of logical processors - which is the same result as calling Environment.ProcessorCount. – Bob Bryan May 04 '19 at 07:13
3

There are many answers here already, but some have heavy upvotes and are incorrect.

The .NET Environment.ProcessorCount WILL return incorrect values and can fail critically if your system WMI is configured incorrectly.

If you want a RELIABLE way to count the cores, the only way is Win32 API.

Here is a C++ snippet:

#include <Windows.h>
#include <vector>

int num_physical_cores()
{
    static int num_cores = []
    {
        DWORD bytes = 0;
        GetLogicalProcessorInformation(nullptr, &bytes);
        std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> coreInfo(bytes / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
        GetLogicalProcessorInformation(coreInfo.data(), &bytes);

        int cores = 0;
        for (auto& info : coreInfo)
        {
            if (info.Relationship == RelationProcessorCore)
                ++cores;
        }
        return cores > 0 ? cores : 1;
    }();
    return num_cores;
}

And since this is a .NET C# Question, here's the ported version:

[StructLayout(LayoutKind.Sequential)]
struct CACHE_DESCRIPTOR
{
    public byte Level;
    public byte Associativity;
    public ushort LineSize;
    public uint Size;
    public uint Type;
}

[StructLayout(LayoutKind.Explicit)]
struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
{
    [FieldOffset(0)] public byte ProcessorCore;
    [FieldOffset(0)] public uint NumaNode;
    [FieldOffset(0)] public CACHE_DESCRIPTOR Cache;
    [FieldOffset(0)] private UInt64 Reserved1;
    [FieldOffset(8)] private UInt64 Reserved2;
}

public enum LOGICAL_PROCESSOR_RELATIONSHIP
{
    RelationProcessorCore,
    RelationNumaNode,
    RelationCache,
    RelationProcessorPackage,
    RelationGroup,
    RelationAll = 0xffff
}

struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
{
    public UIntPtr ProcessorMask;
    public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
    public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
}

[DllImport("kernel32.dll")]
static extern unsafe bool GetLogicalProcessorInformation(SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer, out int bufferSize);

static unsafe int GetProcessorCoreCount()
{
    GetLogicalProcessorInformation(null, out int bufferSize);
    int numEntries = bufferSize / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
    var coreInfo = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[numEntries];

    fixed (SYSTEM_LOGICAL_PROCESSOR_INFORMATION* pCoreInfo = coreInfo)
    {
        GetLogicalProcessorInformation(pCoreInfo, out bufferSize);
        int cores = 0;
        for (int i = 0; i < numEntries; ++i)
        {
            ref SYSTEM_LOGICAL_PROCESSOR_INFORMATION info = ref pCoreInfo[i];
            if (info.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore)
                ++cores;
        }
        return cores > 0 ? cores : 1;
    }
}

public static readonly int NumPhysicalCores = GetProcessorCoreCount();
Jorma Rebane
  • 611
  • 5
  • 11
  • Is it possible to also get the number of physical processors and the number of logical processors? – CTZStef Jul 11 '22 at 14:55
1

One option would be to read the data from the registry. MSDN Article On The Topic: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx)

The processors, I believe can be located here, HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor

    private void determineNumberOfProcessCores()
    {
        RegistryKey rk = Registry.LocalMachine;
        String[] subKeys = rk.OpenSubKey("HARDWARE").OpenSubKey("DESCRIPTION").OpenSubKey("System").OpenSubKey("CentralProcessor").GetSubKeyNames();

        textBox1.Text = "Total number of cores:" + subKeys.Length.ToString();
    }

I am reasonably sure the registry entry will be there on most systems.

Thought I would throw my $0.02 in.

IrishGeek82
  • 355
  • 2
  • 8
  • This will give number of processors that is already available in Environment.ProcessorCount, is there any other similar way to get number of cores for each processor? – Armen Feb 02 '15 at 23:49
0

You can use this class:

public static class CpuCores
{
    private static int cores = 0;
    
    public static int Number
    { 
        get
        {
            if (cores > 0) return cores;

            RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" +
                "{50127dc3-0f36-415e-a6cc-4cb3be910b65}");
            if (key == null)
            {
                cores = Environment.ProcessorCount;
                return cores;
            }
            string[] subkeys = key.GetSubKeyNames();
            key.Close();
            cores = 0;
            if (subkeys != null && subkeys.Length > 0) foreach (string s in subkeys)
            {
                if (s.Length != 4) continue;
                int n;
                if (int.TryParse(s, out n) && ++n > cores) cores = n;
            }
            if (cores <= 0) cores = Environment.ProcessorCount;
            return cores;
        } 
    }
}
Ivan
  • 191
  • 2
  • 4
  • What advantage, if any, is there to reading the registry? If you're just going to fall back to `Environment.ProcessorCount` then why not just use that to begin with? – Lance U. Matthews Aug 03 '22 at 19:08
-2

I was looking for the same thing but I don't want to install any nuget or servicepack, so I found this solution, it is pretty simple and straight forward, using this discussion, I thought it would be so easy to run that WMIC command and get that value, here is the C# code. You only need to use System.Management namespace (and couple more standard namespaces for process and so on).

string fileName = Path.Combine(Environment.SystemDirectory, "wbem", "wmic.exe");
string arguments = @"cpu get NumberOfCores";

Process process = new Process
{
    StartInfo =
    {
        FileName = fileName,
        Arguments = arguments,
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardOutput = true,
        RedirectStandardError = true
    }
};

process.Start();

StreamReader output = process.StandardOutput;
Console.WriteLine(output.ReadToEnd());


process.WaitForExit();
int exitCode = process.ExitCode;
process.Close();
Wang Liang
  • 4,244
  • 6
  • 22
  • 45
Armen
  • 1,083
  • 2
  • 10
  • 18
  • 5
    Not sure why you make a simple WMI query so complicated. Starting the WMI command-line as an external process and parsing its output is really not necessary. .NET has built-in support for WMI queries (System.Management.ManagementObjectSearcher), as some of the other answers here already illustrated. Also, i don't know why you think nuget packages or service packs would be required when using .NET's built-in WMI support instead of wmic.exe... –  Dec 13 '16 at 18:06
  • If someone would like to run C# code on Linux, there is no WMI there. Then we need best a config containing a command line and a regex how to parse the output, but his should fit all operating systems. Or would you better have an Interface IGetProcessorInfo and 2 DLLs (one for Windows, one for Linux) that are delivered with the according install package and loaded dynamically? – BitLauncher Jun 23 '20 at 21:07