Jak znaleźć liczbę rdzeni procesora przez .NET / C #?


317

Czy istnieje sposób .NET / C #, aby dowiedzieć się, ile rdzeni procesora?

PS To proste pytanie kodowe, a nie „Czy powinienem używać wielowątkowości?” pytanie! :-)


7
Czy musisz wiedzieć, ile jest rdzeni lub ile jest procesorów logicznych? Do uruchomienia wielu wątków prawdopodobnie wystarczy jeden z nich, ale istnieją scenariusze, w których różnica może być ważna.
Kevin Kibler

Czy istnieje nowszy sposób na zrobienie tego?
MoonKnight

Odpowiedzi:


477

Istnieje kilka różnych informacji dotyczących procesorów, które można uzyskać:

  1. Liczba procesorów fizycznych
  2. Liczba rdzeni
  3. Liczba procesorów logicznych.

Wszystkie mogą być różne; w przypadku maszyny z 2 dwurdzeniowymi procesorami obsługującymi hiperwątkowanie są 2 procesory fizyczne, 4 rdzenie i 8 procesorów logicznych.

Liczba procesorów logicznych jest dostępna za pośrednictwem klasy Środowisko , ale inne informacje są dostępne tylko za pośrednictwem WMI (może być konieczne zainstalowanie niektórych poprawek lub dodatków Service Pack, aby uzyskać je w niektórych systemach):

Pamiętaj, aby dodać odniesienie w swoim projekcie do System.Management.dll W .NET Core, jest to dostępne (tylko dla Windows) jako pakiet NuGet.

Procesory fizyczne:

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

Rdzenie:

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

Procesory logiczne:

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

LUB

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

Procesory wyłączone z systemu Windows:

Możesz także użyć wywołań API Windows w setupapi.dll, aby odkryć procesory, które zostały wykluczone z systemu Windows (np. Poprzez ustawienia rozruchu) i nie są wykrywalne przy użyciu powyższych środków. Poniższy kod podaje całkowitą liczbę procesorów logicznych (nie byłem w stanie dowiedzieć się, jak odróżnić procesory fizyczne od logicznych), w tym także te, które zostały wykluczone z systemu 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,
}

14
@StingyJack: To prawda, ale chciałbym, żeby były w ładniejszym formacie. Wykrywalność jest dość niska, gdy trzeba budować zapytania typu raw.
Kevin Kibler

5
WMI Code Creator pomoże w wykrywaniu wartości i tworzeniu zapytań (może nawet generować kody pośredniczące w c # / vb.net).
StingyJack,

4
Jest w System.Management.dll. Czy umieściłeś w swoim projekcie odniesienie do tego zestawu?
Kevin Kibler,

2
Drobny problem off-by-one w powyższym kodzie. Ponieważ deviceCountjest liczony od zera, liczba rdzeni powinna być wyprowadzana w następujący sposób:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio

2
Czy nie powodujesz problemów, nie pozbywając się obiektów zarządzania i wyszukiwarek?
Benjamin,

205
Environment.ProcessorCount

[Dokumentacja]


12
To jest tak pięknie proste, że prawie przelewam łzy. Dziękuję za odpowiedź!
MrGreggles,

70
Daje to liczbę procesorów logicznych, a nie liczbę rdzeni.
Kevin Kibler

8
@KevinKibler Na podstawie pytania, podejrzewam, że OP nie rozumie różnicy, a jeśli nie znasz różnicy, prawdopodobnie tego właśnie chcesz.
Glenn Maynard

1
Zwraca to również nieprawidłowe liczenie w wielu podstawowych systemach. Korzystam z dwóch procesorów Dodeca Core z hiperwątkowością, co daje w sumie 48 procesorów logicznych. Environment.ProcessorCountdaje 32.
Allen Clark Copeland Jr

1
@AlexanderMorou, tak, nie zapewni to dokładnych wyników na niektórych serwerach z wieloma procesorami. Jest na to poprawka, ale jeszcze jej nie przetestowałem.
TheLegendaryCopyCoder

35

Zapytania WMI są powolne, więc spróbuj wybrać tylko żądanych członków zamiast wybierać *.

Następujące zapytanie zajmuje 3,4 s:

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

Podczas gdy ten trwa 0,122 sekundy:

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

1
Na jakim systemie to uruchamiasz? I korzystać z wielu „SELECT *” zapytania i nie ma nigdzie blisko 3,4 sekundy, sprawdzone na tysiącach komputerów, że mój program jest rozmieszczonych na. Robię Select *, ponieważ otrzymuję wiele właściwości z obiektu. Robię to jednak trochę inaczej: utwórz obiekt ObjectQuery na Select *; pobierz ManagementObjectCollection; następnie foreach ManagementObject w ManagementObjectCollection.
deegee,

@deegee: masz rację, samo zapytanie nie zajmuje dużo czasu z „Select *”, po prostu int parsowanie poniżej jest powolne, jeśli iterują wszystkie zwrócone wartości zamiast tylko NumberOfCores.
Aleix Mercader


10

Interesujące jest zobaczyć, w jaki sposób .NET sprawia, że ​​jest to co najmniej wewnętrznie mówiąc… To jest tak „proste” jak poniżej:

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


4

Ze źródła .NET Framework

Możesz go również pobrać przy włączonym PInvokeKernel32.dll

Poniższy kod pochodzi mniej więcej SystemInfo.csze źródła System.Web znajdującego się tutaj :

[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;
    }
}

2
Próbowałem tego, ale zwraca liczbę procesorów logicznych - co daje taki sam wynik jak wywołanie Environment.ProcessorCount.
Bob Bryan

1

Jedną z opcji byłoby odczytanie danych z rejestru. Artykuł MSDN na temat: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

Procesory, jak sądzę, mogą być tutaj umieszczone, 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();
    }

Jestem przekonany, że wpis rejestru znajdzie się w większości systemów.

Chociaż wrzuciłbym moje 0,02 $.


To da liczbę procesorów, które są już dostępne w środowisku.ProcessorCount, czy istnieje inny podobny sposób na uzyskanie liczby rdzeni dla każdego procesora?
Armen

0

Poniższy program drukuje logiczne i fizyczne rdzenie komputera z systemem 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
*/

6
To pytanie jest oznaczone .NET; twój kod nie jest kodem .NET.
Wai Ha Lee

-1

Szukałem tego samego, ale nie chcę instalować żadnego nugetu ani pakietu serwisowego, więc znalazłem to rozwiązanie, jest dość proste i proste, korzystając z tej dyskusji, pomyślałem, że tak łatwo będzie uruchomić polecenie WMIC i uzyskaj tę wartość, oto kod C #. Musisz tylko użyć przestrzeni nazw System.Management (i dodać kilka standardowych przestrzeni nazw dla procesu itd.).

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

4
Nie wiem, dlaczego tak proste zapytanie WMI jest tak skomplikowane. Uruchomienie wiersza polecenia WMI jako zewnętrznego procesu i parsowanie jego danych wyjściowych nie jest tak naprawdę konieczne. .NET ma wbudowaną obsługę zapytań WMI (System.Management.ManagementObjectSearcher), jak niektóre inne odpowiedzi tutaj zilustrowane. Nie wiem też, dlaczego uważasz, że pakiety nuget lub dodatki Service Pack byłyby wymagane przy korzystaniu z wbudowanej obsługi WMI .NET zamiast wmic.exe ...
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.