Como encontrar o número de núcleos de CPU via .NET / C #?

317

Existe uma maneira através do .NET / C # para descobrir o número de núcleos da CPU?

PS Esta é uma pergunta de código direta, não uma "Devo usar multi-threading?" questão! :-)

MrGreggles
fonte
7
Você precisa saber quantos núcleos existem ou quantos processadores lógicos existem? Por apenas executar vários encadeamentos, provavelmente é suficiente, mas há cenários em que a diferença pode ser importante.
Kevin Kibler
Existe uma maneira mais nova de fazer isso?
MoonKnight

Respostas:

477

Existem várias informações diferentes relacionadas aos processadores que você pode obter:

  1. Número de processadores físicos
  2. Número de núcleos
  3. Número de processadores lógicos.

Tudo isso pode ser diferente; no caso de uma máquina com 2 processadores habilitados para hyperthreading de núcleo duplo, existem 2 processadores físicos, 4 núcleos e 8 processadores lógicos.

O número de processadores lógicos está disponível na classe Environment , mas as outras informações estão disponíveis apenas no WMI (e talvez você precise instalar alguns hotfixes ou service packs para obtê-lo em alguns sistemas):

Certifique-se de adicionar uma referência no seu projeto ao System.Management.dll No .NET Core, isso está disponível (apenas para Windows) como um pacote NuGet.

Processadores físicos:

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

Processadores lógicos:

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

OU

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

Processadores excluídos do Windows:

Você também pode usar as chamadas da API do Windows em setupapi.dll para descobrir os processadores que foram excluídos do Windows (por exemplo, através das configurações de inicialização) e não são detectáveis ​​usando os meios acima. O código abaixo fornece o número total de processadores lógicos (não consegui descobrir como diferenciar processadores físicos de lógicos) existentes, incluindo aqueles que foram excluídos do 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
fonte
14
@StingyJack: Verdade, mas eu gostaria que estivesse em um formato melhor. A capacidade de descoberta é muito baixa quando você precisa criar consultas de sequência bruta.
Kevin Kibler
5
O WMI Code Creator ajudará na descoberta de valor e na criação de consultas (ele pode até gerar stubs em c # / vb.net).
StingyJack
4
Está no System.Management.dll. Você incluiu uma referência a essa montagem em seu projeto?
Kevin Kibler
2
Problema secundário menor no código acima. Desde deviceCounté baseado em zero, a contagem de núcleos deve ser uma saída como esta:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio
2
Você não está causando problemas por não descartar os objetos de gerenciamento e os pesquisadores?
Benjamin
205
Environment.ProcessorCount

[Documentação]

Sam Harwell
fonte
12
Isso é tão lindamente simples que estou quase derramando lágrimas. Obrigado pela resposta!
MrGreggles # 9/10/09
70
Isso fornece o número de processadores lógicos, não o número de núcleos.
Kevin Kibler
8
@KevinKibler Da pergunta, suspeito que o OP não entenda a diferença, e se você não souber a diferença, provavelmente é isso que você deseja.
Glenn Maynard
1
Isso também retorna a contagem incorreta em muitos sistemas principais. Estou executando dois processadores core dodeca com hyper-threading, o que me dá um total de 48 processadores lógicos. Environment.ProcessorCountretorna 32.
Allen Clark Copeland Jr
1
@AlexanderMorou, sim, isso não fornecerá resultados precisos em alguns servidores com várias CPUs. Existe uma correção para isso, mas ainda não a testamos.
TheLegendaryCopyCoder
35

As consultas WMI são lentas, portanto, tente selecionar apenas os membros desejados em vez de usar o Select *.

A consulta a seguir leva 3.4s:

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

Enquanto este leva 0,122s:

foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())
Aleix Mercader
fonte
1
Em que sistema você está executando isso? Uso várias consultas "Select *" e não demora nem 3,4 segundos, testadas em milhares de computadores nos quais meu software está implantado. Eu faço um Select * porque estou obtendo várias propriedades do objeto. No entanto, faço um pouco diferente: crie uma ObjectQuery no Select *; obtenha o ManagementObjectCollection; em seguida, foreach ManagementObject no ManagementObjectCollection.
deegee
@deegee: você está certo, a consulta em si não leva muito mais tempo com "Select *", mas a análise int abaixo é lenta se iterar todos os valores retornados, em vez de apenas NumberOfCores.
Aleix Mercader 13/09
19

Environment.ProcessorCount deve fornecer o número de núcleos na máquina local.

Mithrax
fonte
56
Isso fornece o número de processadores lógicos, não o número de núcleos.
Kevin Kibler
10

É bastante interessante ver como o .NET faz isso internamente para dizer o mínimo ... É tão "simples" quanto abaixo:

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

A maneira mais fácil = Environment.ProcessorCount
Exemplo de Propriedade 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
fonte
O método Environment.ProcessorCount às vezes mostra dados incorretos (consulte stackoverflow.com/questions/27965962/… )
construtor
4

Da fonte do .NET Framework

Você também pode obtê-lo com o PInvoke noKernel32.dll

O código a seguir vem mais ou menos da SystemInfo.csfonte System.Web localizada aqui :

[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;
    }
}
Fab
fonte
2
Tentei isso, mas ele retorna o número de processadores lógicos - que é o mesmo resultado que chamar Environment.ProcessorCount.
Bob Bryan
1

Uma opção seria ler os dados do registro. Artigo do MSDN sobre o tópico: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

Os processadores, acredito, podem estar localizados aqui, 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();
    }

Tenho certeza de que a entrada do registro estará presente na maioria dos sistemas.

Embora eu jogasse meus US $ 0,02.

IrishGeek82
fonte
Isso fornecerá o número de processadores que já estão disponíveis no Environment.ProcessorCount, existe outra maneira semelhante de obter o número de núcleos para cada processador?
Armen
0

O programa a seguir imprime os núcleos lógicos e físicos de uma 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
fonte
6
Esta pergunta está etiquetada como .NET; seu código não é código .NET.
Wai Ha Lee
-1

Eu estava procurando a mesma coisa, mas não quero instalar nenhum nuget ou service pack, então encontrei esta solução, é bastante simples e direta, usando esta discussão, achei que seria tão fácil executar esse comando WMIC e obtenha esse valor, aqui está o código C #. Você só precisa usar o espaço para nome System.Management (e acoplar mais espaços para nome padrão para processo e assim por diante).

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
fonte
4
Não sei por que você faz uma consulta WMI simples tão complicada. Iniciar a linha de comando WMI como um processo externo e analisar sua saída realmente não é necessário. O .NET possui suporte interno para consultas WMI (System.Management.ManagementObjectSearcher), como algumas das outras respostas aqui já ilustradas. Além disso, eu não sei por que você acha pacotes NuGet ou service packs seria necessária quando se utiliza do .NET suporte embutido WMI em vez de Wmic.exe ...