In the realm of C# programming, understanding the underlying hardware can be crucial for optimizing performance. One fundamental aspect is knowing the number of CPU cores available. In this article, we will explore methods to retrieve the number of CPU cores in C#, delving into the System.Environment class and Windows WMI.

To download the source code for this article, you can visit our GitHub repository.

Let’s start.

Retrieve CPU Core Information

First of all, there are three different terms, when it comes to CPU core count. There are physical processors, CPU cores, and logical processors. They differ in the amount and therefore have different meanings.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Physical processors represent the tangible chips installed on the motherboard, each containing multiple CPU cores capable of executing instructions independently. Logical processors, on the other hand, are virtual entities generated by technologies like hyper-threading. Each core is capable of handling multiple threads concurrently. Often the amount of logical processors is double of its physical processors.

We begin with the simplest way, to retrieve the amount of logical processors:

var cpuCount = Environment.ProcessorCount;

This succinct method provides a rapid means to retrieve the count of logical processors. Notably, this method stands as the singular approach applicable across all platforms for retrieving CPU core information, ensuring uniformity and ease of access.

Retrieve WMI Cores Information

WMI (Windows Management Instrumentation) Core Information is another method we can use to retrieve the number of CPU cores in C#, but it operates exclusively on Windows machines. This limitation poses risks, especially considering many servers run on Linux. When developing open-source code or libraries, independence is crucial. Ensuring accessibility across different operating systems allows anyone to utilize and test the application. However, in specific scenarios, leveraging WMI may prove beneficial.

WMI Core information typically originates from the operating system’s management infrastructure, providing a standardized interface for retrieving data about hardware, software, and system configuration within Windows environments. With this interface, we can get advanced CPU information. If we want to retrieve the number of physical processors or the number of cores on a Windows system, we use the WMI interface.

First of all, we add the NuGet package System.Management.dll to our project references:

Install-Package System.Management

With that, let’s look at how we retrieve the number of cores.

Retrieve the Number of Cores

Now we can retrieve the number of cores:

public int GetNumberOfCores()
{
    if (!OperatingSystem.IsWindows())
    {
        throw new PlatformNotSupportedException();
    }

    var coreCount = 0;
    const string wmiQuery = "Select * from Win32_Processor";
    foreach (var item in new System.Management.ManagementObjectSearcher(wmiQuery).Get())
    {
        coreCount += int.Parse(item["NumberOfCores"].ToString());
    }

    return coreCount;
}

Here, we define the GetNumberOfCores() method. If the platform is not Windows, we throw a PlatformNotSupportedException. Otherwise, we continue to count the total number of cores.

The System.Management.ManagementObjectSearcher class is the WMI API that we use to query for the Windows system information. We loop over the query result and retrieve the NumberOfCores property from WMI instance.

Physical Processors

We can also get the number of physical processors:

const string wmiQuery = "Select * from Win32_ComputerSystem";
public int GetPhysicalProcessors()
{
    if (!OperatingSystem.IsWindows())
    {
        throw new PlatformNotSupportedException();
    }

    foreach (var item in new System.Management.ManagementObjectSearcher(wmiQuery).Get())
    {
        return int.Parse(item["NumberOfProcessors"].ToString());
    }

    return 0;
}

Here, we use a for loop to get all the results of executing the query Select * from Win32_ComputerSystem in System.Management.ManagementObjectSearcher. Then, we retrieve the NumberOfProcessors attribute for each result, which represents the count of physical processors present in the system.

Logical Processors

Instead of using the Environment.ProcessorCount we can also use this API to get the number of logical processors:

const string wmiQuery = "Select * from Win32_ComputerSystem";
public int GetLogicalProcessors()
{
    if (!OperatingSystem.IsWindows())
    {
        throw new PlatformNotSupportedException();
    }

    foreach (var item in new System.Management.ManagementObjectSearcher(wmiQuery).Get())
    {
        return int.Parse(item["NumberOfLogicalProcessors"].ToString());
    }

    return 0;
}

Similar to our previous method, we use a loop to get the results from the ManagementObjectSearcher() method. This time, we retrieve the NumberOfLogicalProcessors attribute, representing the count of logical processors in the system.

Utilizing WMI for CPU core information retrieval provides advanced insights into system hardware but is limited to Windows environments, posing challenges for cross-platform compatibility. While WMI offers a standardized interface for accessing CPU details like physical processors and cores, its reliance on Windows-specific APIs requires careful consideration in open-source projects aiming for broader platform support.

Processors Excluded from Windows

When dealing with CPU information in the context of Windows systems, it’s essential to recognize that Windows treats processors differently. Compatibility issues, boot settings, or other factors may exclude some processors from certain functionalities or features. Specific Windows versions, such as Windows 11, may exclude certain CPUs due to compatibility requirements.

To delve deeper into processor exclusions, we can use Windows API calls available in setupapi.dll. These calls allow us to discover processors that have been explicitly excluded from Windows. We first need to create some functions and structures, to get such processors:

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

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

[DllImport("setupapi.dll", SetLastError = true)]
private static extern bool SetupDiEnumDeviceInfo(IntPtr DeviceInfoSet,
    int 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,
}

Here, we include imports (DllImport) for functions from setupapi.dll, a structure (SP_DEVINFO_DATA), and an enumeration (DIGCF). These are functions and types we need to be able to get the number of excluded processors.

Now, we identify the excluded processors:

public int GetExcludedProcessors()
{
    if (!OperatingSystem.IsWindows())
    {
        throw new PlatformNotSupportedException();
    }

    var deviceCount = 0;
    var deviceList = IntPtr.Zero;
    var processorGuid = new Guid("{50127dc3-0f36-415e-a6cc-4cb3be910b65}");

    try
    {
        deviceList = SetupDiGetClassDevs(ref processorGuid, "ACPI", IntPtr.Zero, (int)DIGCF.PRESENT);
        for (var deviceNumber = 0; ; deviceNumber++)
        {
            var deviceInfo = new SP_DEVINFO_DATA();
            deviceInfo.cbSize = Marshal.SizeOf(deviceInfo);

            if (!SetupDiEnumDeviceInfo(deviceList, deviceNumber, ref deviceInfo))
            {
                deviceCount = deviceNumber;
                break;
            }
        }
    }
    finally
    {
        if (deviceList != IntPtr.Zero) { SetupDiDestroyDeviceInfoList(deviceList); }
    }

    return deviceCount;
}

Here, we define the GetExcludedProcessors() method, which we use to retrieve the count of excluded processors on a Windows system using SetupApi calls on Windows. 

We start by initializing variables and fetching device information using the SetupDiGetClassDevs() method. Then we enumerate through the devices and update the count. Finally, we clean up resources and return the count of processors. Note that our method provides the total number of logical processors, including those excluded from Windows.

Application and Optimization

When working with CPU information in C#, understanding how to optimize performance is crucial. Multithreading is one way to optimize the performance of a program. But to optimize a multithreaded code, we want to have at least as many threads as the number of logical processors.

By utilizing this approach, we can maximize CPU usage because every processor has assigned tasks, and we distribute the workload across CPU cores for faster execution. Remember that CPU optimization is a balance between performance and power efficiency. By leveraging core information effectively, we create more efficient applications that optimally use available resources.

Conclusion

In C# programming, optimizing performance often hinges on understanding CPU core availability. We explored physical processors, CPU cores, and logical processors, each with unique significance. Through C# and Windows WMI, we discovered methods to retrieve the number of CPU cores, from Environment.ProcessorCount to WMI queries. Furthermore, we discussed the complexities of processor exclusion from Windows systems.

It’s crucial to harness CPU core data for application optimization. Aligning multithreaded code with logical processor counts maximizes system potential, balancing performance and power efficiency.

Mastering CPU core retrieval in C# empowers developers to craft efficient applications, maximizing resource utilization across diverse computing environments.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!