Wie finde ich die Anzahl der CPU-Kerne über .NET / C #?

317

Gibt es eine Möglichkeit über .NET / C # , die Anzahl der CPU-Kerne herauszufinden?

PS Dies ist eine reine Codefrage, keine "Soll ich Multithreading verwenden?" Frage! :-)

MrGreggles
quelle
7
Müssen Sie wissen, wie viele Kerne es gibt oder wie viele logische Prozessoren es gibt? Für das Ausführen mehrerer Threads ist wahrscheinlich beides ausreichend, aber es gibt Szenarien, in denen der Unterschied wichtig sein kann.
Kevin Kibler
Gibt es einen neueren Weg, dies zu tun?
MoonKnight

Antworten:

477

Es gibt verschiedene Informationen zu Prozessoren, die Sie erhalten können:

  1. Anzahl der physischen Prozessoren
  2. Zahl der Kerne
  3. Anzahl der logischen Prozessoren.

Diese können alle unterschiedlich sein; Bei einem Computer mit 2 Dual-Core-Hyper-Threading-fähigen Prozessoren gibt es 2 physische Prozessoren, 4 Kerne und 8 logische Prozessoren.

Die Anzahl der logischen Prozessoren ist über die Environment- Klasse verfügbar , die anderen Informationen sind jedoch nur über WMI verfügbar (und Sie müssen möglicherweise einige Hotfixes oder Service Packs installieren , um sie auf einigen Systemen abzurufen):

Stellen Sie sicher, dass Sie in Ihrem Projekt einen Verweis auf System.Management.dll hinzufügen In .NET Core ist dieser (nur für Windows) als NuGet-Paket verfügbar.

Physikalische Prozessoren:

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

Kerne:

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

Logische Prozessoren:

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

ODER

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

Von Windows ausgeschlossene Prozessoren:

Sie können auch Windows-API-Aufrufe in setupapi.dll verwenden , um Prozessoren zu ermitteln, die von Windows ausgeschlossen wurden (z. B. durch Starteinstellungen ) und mit den oben genannten Mitteln nicht erkannt werden können. Der folgende Code gibt die Gesamtzahl der vorhandenen logischen Prozessoren an (ich konnte nicht herausfinden, wie physische von logischen Prozessoren unterschieden werden können), einschließlich derjenigen, die von Windows ausgeschlossen wurden:

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
quelle
14
@StingyJack: Stimmt, aber ich wünschte, es wäre in einem schöneren Format. Die Erkennbarkeit ist ziemlich gering, wenn Sie rohe Zeichenfolgenabfragen erstellen müssen.
Kevin Kibler
5
WMI Code Creator hilft bei der Werterkennung und Abfrageerstellung (es können sogar Stubs in c # / vb.net generiert werden).
StingyJack
4
Es ist in System.Management.dll. Haben Sie in Ihrem Projekt einen Verweis auf diese Baugruppe aufgenommen?
Kevin Kibler
2
Kleines Off-by-One-Problem im obigen Code. Da deviceCountes auf Null basiert, sollte die Kernanzahl wie folgt ausgegeben werden:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio
2
Verursachen Sie keine Probleme, indem Sie die Verwaltungsobjekte und Sucher nicht entsorgen?
Benjamin
205
Environment.ProcessorCount

[Dokumentation]

Sam Harwell
quelle
12
Das ist so schön einfach, dass ich fast Tränen vergieße. Danke für die Antwort!
MrGreggles
70
Dies gibt die Anzahl der logischen Prozessoren an, nicht die Anzahl der Kerne.
Kevin Kibler
8
@ KevinKibler Aus der Frage, ich vermute, das OP versteht den Unterschied nicht, und wenn Sie den Unterschied nicht kennen, ist dies wahrscheinlich das, was Sie wollen.
Glenn Maynard
1
Dies gibt auch bei vielen Kernsystemen die falsche Anzahl zurück. Ich verwende zwei Dodeca-Core-Prozessoren mit Hyper-Threading, wodurch ich insgesamt 48 logische Prozessoren habe. Environment.ProcessorCountergibt 32.
Allen Clark Copeland Jr
1
@AlexanderMorou, ja, dies liefert auf einigen Multi-CPU-Servern keine genauen Ergebnisse. Es gibt eine Lösung dafür, aber noch nicht getestet.
TheLegendaryCopyCoder
35

WMI-Abfragen sind langsam. Versuchen Sie daher, nur die gewünschten Mitglieder auszuwählen, anstatt Select * zu verwenden.

Die folgende Abfrage dauert 3,4 Sekunden:

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

Während dieser 0,122s dauert:

foreach (var item in new System.Management.ManagementObjectSearcher("Select NumberOfCores from Win32_Processor").Get())
Aleix Mercader
quelle
1
Auf welchem ​​System läuft das? Ich verwende mehrere "Select *" - Abfragen und es dauert nicht annähernd 3,4 Sekunden, getestet auf Tausenden von Computern, auf denen meine Software bereitgestellt wird. Ich mache eine Auswahl *, weil ich mehrere Eigenschaften vom Objekt erhalte. Ich mache es jedoch etwas anders: Erstellen Sie eine ObjectQuery auf dem Select *; Holen Sie sich die ManagementObjectCollection; dann für jedes ManagementObject in der ManagementObjectCollection.
Deegee
@deegee: Sie haben Recht, die Abfrage selbst dauert mit "Select *" nicht viel länger. Es ist nur so, dass das unten stehende int-Parsing langsam ist, wenn alle zurückgegebenen Werte anstelle von nur NumberOfCores wiederholt werden.
Aleix Mercader
19

Environment.ProcessorCount sollte die Anzahl der Kerne auf dem lokalen Computer angeben.

Mithrax
quelle
56
Dies gibt die Anzahl der logischen Prozessoren an, nicht die Anzahl der Kerne.
Kevin Kibler
10

Es ist ziemlich interessant zu sehen, wie .NET dies intern schafft, um es gelinde auszudrücken ... Es ist so "einfach" wie unten:

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
quelle
6

Der einfachste Weg = Environment.ProcessorCount
Beispiel aus der Environment.ProcessorCount-Eigenschaft

using System;

class Sample 
{
    public static void Main() 
    {
        Console.WriteLine("The number of processors " +
            "on this computer is {0}.", 
            Environment.ProcessorCount);
    }
}
Sanfour ben sanfer
quelle
Methode Environment.ProcessorCount zeigt manchmal falsche Daten an (siehe stackoverflow.com/questions/27965962/… )
Konstruktor
4

Aus der .NET Framework-Quelle

Sie können es auch mit PInvoke erhalten aufKernel32.dll

Der folgende Code stammt mehr oder weniger aus SystemInfo.csder System.Web-Quelle, die sich hier befindet :

[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
quelle
2
Versuchte dies, aber es gibt die Anzahl der logischen Prozessoren zurück - das ist das gleiche Ergebnis wie beim Aufrufen von Environment.ProcessorCount.
Bob Bryan
1

Eine Möglichkeit wäre, die Daten aus der Registrierung zu lesen. MSDN-Artikel zum Thema: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

Ich glaube, die Prozessoren befinden sich hier, 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();
    }

Ich bin mir ziemlich sicher, dass der Registrierungseintrag auf den meisten Systemen vorhanden sein wird.

Obwohl ich meine $ 0,02 in werfen würde.

IrishGeek82
quelle
Dies gibt die Anzahl der Prozessoren an, die bereits in Environment.ProcessorCount verfügbar sind. Gibt es eine andere ähnliche Möglichkeit, die Anzahl der Kerne für jeden Prozessor abzurufen?
Armen
0

Das folgende Programm druckt die logischen und physischen Kerne eines Windows-Computers.

#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
quelle
6
Diese Frage ist mit .NET gekennzeichnet. Ihr Code ist kein .NET-Code.
Wai Ha Lee
-1

Ich habe nach dem gleichen gesucht, aber ich möchte kein Nuget oder Servicepack installieren. Deshalb habe ich diese Lösung gefunden. Sie ist ziemlich einfach und unkompliziert. Mit dieser Diskussion dachte ich, es wäre so einfach, diesen WMIC-Befehl auszuführen und erhalten Sie diesen Wert, hier ist der C # -Code. Sie müssen nur den System.Management-Namespace verwenden (und weitere Standard-Namespaces für den Prozess usw. koppeln).

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
quelle
4
Ich bin mir nicht sicher, warum Sie eine einfache WMI-Abfrage so kompliziert machen. Das Starten der WMI-Befehlszeile als externer Prozess und das Parsen der Ausgabe ist nicht unbedingt erforderlich. .NET bietet integrierte Unterstützung für WMI-Abfragen (System.Management.ManagementObjectSearcher), wie einige der anderen hier bereits veranschaulichten Antworten zeigen. Außerdem weiß ich nicht, warum Sie glauben, dass Nuget-Pakete oder Service Packs erforderlich sind, wenn Sie die integrierte WMI-Unterstützung von .NET anstelle von wmic.exe verwenden ...