¿Cómo encontrar el número de núcleos de CPU a través de .NET / C #?

317

¿Hay alguna manera a través de .NET / C # para averiguar la cantidad de núcleos de CPU?

PD Esta es una pregunta de código directo, no un "¿Debo usar subprocesos múltiples? ¡pregunta! :-)

MrGreggles
fuente
77
¿Necesita saber cuántos núcleos hay o cuántos procesadores lógicos hay? Para ejecutar múltiples subprocesos, probablemente sea suficiente, pero hay escenarios en los que la diferencia podría ser importante.
Kevin Kibler
¿Hay alguna forma más nueva de hacer esto?
MoonKnight

Respuestas:

477

Hay varias piezas diferentes de información relacionadas con los procesadores que puede obtener:

  1. Número de procesadores físicos
  2. Numero de nucleos
  3. Número de procesadores lógicos.

Todos estos pueden ser diferentes; en el caso de una máquina con 2 procesadores habilitados para hyper-threading de doble núcleo, hay 2 procesadores físicos, 4 núcleos y 8 procesadores lógicos.

El número de procesadores lógicos está disponible a través de la clase Entorno , pero la otra información solo está disponible a través de WMI (y es posible que tenga que instalar algunas revisiones o paquetes de servicio para obtenerlo en algunos sistemas):

Asegúrese de agregar una referencia en su proyecto a System.Management.dll En .NET Core, está disponible (solo para Windows) como un paquete NuGet.

Procesadores Físicos:

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

Núcleos:

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);

Procesadores Lógicos:

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

O

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

Procesadores excluidos de Windows:

También puede usar llamadas API de Windows en setupapi.dll para descubrir procesadores que han sido excluidos de Windows (por ejemplo, a través de la configuración de arranque) y que no son detectables utilizando los medios anteriores. El siguiente código proporciona el número total de procesadores lógicos (no he podido averiguar cómo diferenciar los procesadores físicos de los lógicos) que existen, incluidos los que han sido excluidos de 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,
}
Kevin Kibler
fuente
14
@StingyJack: Cierto, pero desearía que estuviera en un mejor formato. La capacidad de detección es bastante baja cuando tiene que construir consultas de cadena sin procesar.
Kevin Kibler
55
WMI Code Creator ayudará con el descubrimiento de valor y la creación de consultas (incluso puede generar apéndices en c # / vb.net).
StingyJack
44
Está en System.Management.dll. ¿Incluyó una referencia a ese ensamblaje en su proyecto?
Kevin Kibler
2
Problema menor por uno en el código anterior. Como deviceCountse basa en cero, el recuento de núcleos se debe generar de esta manera:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio
2
¿No está causando problemas al no disponer de los objetos de gestión y buscadores?
Benjamin
205
Environment.ProcessorCount

[Documentación]

Sam Harwell
fuente
12
Eso es tan maravillosamente simple que casi derramo lágrimas. Gracias por la respuesta!
MrGreggles
70
Esto proporciona la cantidad de procesadores lógicos, no la cantidad de núcleos.
Kevin Kibler
8
@KevinKibler De la pregunta, sospecho que el OP no entiende la diferencia, y si no sabe la diferencia, esto es probablemente lo que quiere.
Glenn Maynard
1
Esto también devuelve el recuento incorrecto en muchos sistemas centrales. Estoy ejecutando dos procesadores dodeca core con hyper-threading, lo que me da un total de 48 procesadores lógicos. Environment.ProcessorCountrinde 32.
Allen Clark Copeland Jr
1
@AlexanderMorou, sí, esto no proporcionará resultados precisos en algunos servidores con múltiples CPU. Hay una solución para esto, pero aún no lo he probado.
TheLegendaryCopyCoder
35

Las consultas de WMI son lentas, así que intente seleccionar solo los miembros deseados en lugar de usar Seleccionar *.

La siguiente consulta toma 3.4s:

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

Si bien este toma 0.122s:

foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())
Aleix Mercader
fuente
1
¿En qué sistema estás ejecutando esto? Utilizo varios "SELECT *" consultas y no se necesita en cualquier lugar cerca de 3,4 segundos, probados en miles de ordenadores que el software se implementa en. Hago un Select * porque obtengo varias propiedades del objeto. Sin embargo, lo hago un poco diferente: crear una ObjectQuery en Select *; obtener el ManagementObjectCollection; luego foreach ManagementObject en ManagementObjectCollection.
deegee
@deegee: tienes razón, la consulta en sí no toma mucho más tiempo con "Seleccionar *", es solo que el análisis int a continuación es lento si se repiten todos los valores devueltos en lugar de solo NumberOfCores.
Aleix Mercader
19

Environment.ProcessorCount debería proporcionarle el número de núcleos en la máquina local.

Mitrax
fuente
56
Esto proporciona la cantidad de procesadores lógicos, no la cantidad de núcleos.
Kevin Kibler
10

Es bastante interesante ver cómo .NET consigue esto internamente por decir lo menos ... Es tan "simple" como se muestra a continuación:

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
fuente
7

La forma más fácil = Ejemplar Environment.ProcessorCount
de la propiedad Environment.ProcessorCount

using System;

class Sample 
{
    public static void Main() 
    {
        Console.WriteLine("The number of processors " +
            "on this computer is {0}.", 
            Environment.ProcessorCount);
    }
}
Sanfour ben sanfer
fuente
Método Environment.ProcessorCount a veces muestra datos incorrectos (consulte stackoverflow.com/questions/27965962/… )
constructor
4

De la fuente de .NET Framework

También puede obtenerlo con PInvoke enKernel32.dll

El siguiente código proviene más o menos de la SystemInfo.csfuente System.Web ubicada aquí :

[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;
    }
}
Fabuloso
fuente
2
Intenté esto, pero devuelve el número de procesadores lógicos, que es el mismo resultado que llamar a Environment.ProcessorCount.
Bob Bryan
1

Una opción sería leer los datos del registro. Artículo de MSDN sobre el tema: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

Los procesadores, creo que pueden ubicarse aquí, 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();
    }

Estoy razonablemente seguro de que la entrada del registro estará allí en la mayoría de los sistemas.

Aunque tiraría mis $ 0.02.

IrishGeek82
fuente
Esto le dará una cantidad de procesadores que ya está disponible en Environment.ProcessorCount, ¿hay alguna otra forma similar de obtener la cantidad de núcleos para cada procesador?
Armen
0

El siguiente programa imprime los núcleos lógicos y físicos de una máquina Windows.

#define STRICT
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <omp.h>

template<typename T>
T *AdvanceBytes(T *p, SIZE_T cb)
{
 return reinterpret_cast<T*>(reinterpret_cast<BYTE *>(p) + cb);
}

class EnumLogicalProcessorInformation
{
public:
 EnumLogicalProcessorInformation(LOGICAL_PROCESSOR_RELATIONSHIP Relationship)
  : m_pinfoBase(nullptr), m_pinfoCurrent(nullptr), m_cbRemaining(0)
 {
  DWORD cb = 0;
  if (GetLogicalProcessorInformationEx(Relationship,
                                       nullptr, &cb)) return;
  if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) return;

  m_pinfoBase =
   reinterpret_cast<SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *>
                                     (LocalAlloc(LMEM_FIXED, cb));
  if (!m_pinfoBase) return;

  if (!GetLogicalProcessorInformationEx(Relationship, 
                                        m_pinfoBase, &cb)) return;

  m_pinfoCurrent = m_pinfoBase;
  m_cbRemaining = cb;
 }

 ~EnumLogicalProcessorInformation() { LocalFree(m_pinfoBase); }

 void MoveNext()
 {
  if (m_pinfoCurrent) {
   m_cbRemaining -= m_pinfoCurrent->Size;
   if (m_cbRemaining) {
    m_pinfoCurrent = AdvanceBytes(m_pinfoCurrent,
                                  m_pinfoCurrent->Size);
   } else {
    m_pinfoCurrent = nullptr;
   }
  }
 }

 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *Current()
                                         { return m_pinfoCurrent; }
private:
 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *m_pinfoBase;
 SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *m_pinfoCurrent;
 DWORD m_cbRemaining;
};


int __cdecl main(int argc, char **argv)
{
  int numLogicalCore = 0;
  int numPhysicalCore = 0;

  for (EnumLogicalProcessorInformation enumInfo(RelationProcessorCore);
      auto pinfo = enumInfo.Current(); enumInfo.MoveNext()) 
  {
      int numThreadPerCore = (pinfo->Processor.Flags == LTP_PC_SMT) ? 2 : 1;
      // std::cout << "thread per core: "<< numThreadPerCore << std::endl;
      numLogicalCore += numThreadPerCore;
      numPhysicalCore += 1;
  }

  printf ("Number of physical core = %d , Number of Logical core = %d \n", numPhysicalCore, numLogicalCore );

 char c = getchar(); /* just to wait on to see the results in the command prompt */
 return 0;
}

/*
I tested with Intel Xeon four cores with hyper threading and here is the result
Number of physical core = 4 , Number of Logical core = 8
*/
Arumugam Muruganantham
fuente
66
Esta pregunta está etiquetada .NET; su código no es código .NET.
Wai Ha Lee
-1

Estaba buscando lo mismo pero no quiero instalar ningún nuget o servicepack, así que encontré esta solución, es bastante simple y directo, usando esta discusión, pensé que sería tan fácil ejecutar ese comando WMIC y obtener ese valor, aquí está el código C #. Solo necesita usar el espacio de nombres System.Management (y unir más espacios de nombres estándar para el proceso, etc.).

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();
Armen
fuente
44
No estoy seguro de por qué hacer una consulta WMI simple tan complicada. Iniciar la línea de comandos WMI como un proceso externo y analizar su salida realmente no es necesario. .NET tiene soporte incorporado para consultas WMI (System.Management.ManagementObjectSearcher), como algunas de las otras respuestas aquí ya ilustran. Además, no sé por qué cree que se necesitarían paquetes NuGet o paquetes de servicio al utilizar .NET de soporte integrado de WMI en lugar de wmic.exe ...