.NET / C # üzerinden CPU Çekirdeği Sayısı nasıl bulunur?


317

.NET / C # üzerinden CPU çekirdeği sayısını öğrenmenin bir yolu var mı ?

PS Bu düz kod sorusu, "Ben çok iş parçacığı kullanmalıyım?" soru! :-)


7
Kaç tane çekirdek olduğunu veya kaç tane mantıksal işlemci olduğunu bilmek ister misiniz? Sadece birden fazla iş parçacığı çalıştırmak için, her ikisi de yeterlidir, ancak farkın önemli olabileceği senaryolar vardır.
Kevin Kibler

Bunu yapmanın daha yeni bir yolu var mı?
MoonKnight

Yanıtlar:


477

İşlemcilerle ilgili alabileceğiniz birkaç farklı bilgi vardır:

  1. Fiziksel işlemci sayısı
  2. Çekirdek sayısı
  3. Mantıksal işlemci sayısı.

Bunların hepsi farklı olabilir; 2 çift çekirdekli hiper iş parçacığı özellikli işlemciye sahip bir makine söz konusu olduğunda, 2 fiziksel işlemci, 4 çekirdek ve 8 mantıksal işlemci vardır.

Mantıksal işlemci sayısı, Ortam sınıfı aracılığıyla kullanılabilir , ancak diğer bilgiler yalnızca WMI aracılığıyla kullanılabilir (ve bazı sistemlerde almak için bazı düzeltmeler veya hizmet paketleri yüklemeniz gerekebilir ):

Projenize System.Management.dll dosyasına bir başvuru eklediğinizden emin olun . .NET Core'da bu, NuGet paketi olarak kullanılabilir (yalnızca Windows için).

Fiziksel İşlemciler:

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

çekirdekler:

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

Mantıksal İşlemciler:

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

VEYA

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

Windows dışında bırakılan işlemciler:

Windows'tan hariç tutulan (örn. Önyükleme ayarları aracılığıyla) işlemcileri bulmak ve yukarıdaki yöntemleri kullanarak algılanamayan işlemcileri bulmak için setupapi.dll'deki Windows API çağrılarını da kullanabilirsiniz . Aşağıdaki kod, Windows'tan hariç tutulanlar da dahil olmak üzere mevcut mantıksal işlemcilerin toplam sayısını verir (fiziksel olanı mantıksal işlemcilerden nasıl ayırt edemediğimi anlayamadım):

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: Doğru, ama keşke daha hoş bir formatta olsaydı. Ham dize sorguları oluşturmanız gerektiğinde keşfedilebilirlik oldukça düşüktür.
Kevin Kibler

5
WMI Kod Oluşturucu değer bulma ve sorgu oluşturma konusunda yardımcı olacaktır (c # / vb.net'te saplamalar bile oluşturabilir).
StingyJack

4
System.Management.dll içinde. Projenize bu derlemeye bir referans eklediniz mi?
Kevin Kibler

2
Yukarıdaki kodda küçük tek tek sorun. Yana deviceCount: sıfır dayanır, çekirdek sayısı, bu gibi çıkış olmalıdırConsole.WriteLine("Number of cores: {0}", deviceCount + 1);
Francis Litterio

2
Yönetim nesnelerini ve arama görevlilerini atmadan sorunlara neden olmuyor musunuz?
Benjamin

205
Environment.ProcessorCount

[Belge]


12
Bu çok güzel, neredeyse gözyaşı döküyorum. Cevap için teşekkürler!
MrGreggles

70
Bu, çekirdek sayısını değil, mantıksal işlemci sayısını verir.
Kevin Kibler

8
@KevinKibler Sorudan, OP'nin farkı anlamadığından şüpheleniyorum ve farkı bilmiyorsanız, bu muhtemelen istediğiniz şeydir.
Glenn Maynard

1
Bu aynı zamanda birçok çekirdek sistemde yanlış sayımı döndürür. Hiper iş parçacıklı iki dodeca çekirdekli işlemci çalıştırıyorum, bu da bana toplam 48 mantıksal işlemci veriyor. Environment.ProcessorCountgetirisi 32.
Allen Clark Copeland Jr

1
@AlexanderMorou, evet bu bazı çoklu CPU sunucularında doğru sonuçlar veremez. Bunun için bir düzeltme var, ancak sığınak henüz test etmedi.
TheLegendaryCopyCoder

35

WMI sorguları yavaştır, bu nedenle Select * kullanmak yerine yalnızca istenen üyeleri seçmeyi deneyin.

Aşağıdaki sorgu 3.4s alır:

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

Bu 0.122s alırken:

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

1
Bunu hangi sistemde kullanıyorsunuz? Birden çok "Seç *" sorgusu kullanıyorum ve yazılımımın dağıtıldığı binlerce bilgisayarda test edilen, 3,4 saniyeye yakın bir yere gitmiyor . Ben bir Select * yapmak çünkü nesneden birden fazla özellik alıyorum. Ancak, biraz farklı: Select * bir ObjectQuery oluşturmak; ManagementObjectCollection'ı edinin; daha sonra ManagementObjectCollection içinde foreach ManagementObject.
deegee

@deegee: haklısın, sorgunun kendisi "Seç *" ile daha uzun sürmüyor, sadece NumberOfCores yerine döndürülen tüm değerleri yineliyorsa aşağıdaki int ayrıştırma işlemi yavaş.
Aleix Mercader


10

.NET'in bunu en azından söylemek için dahili olarak nasıl aldığını görmek oldukça ilginç ... Aşağıdaki gibi "basit":

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

.NET Framework kaynağından

Ayrıca ile alabilirsiniz PInvoke üzerindeKernel32.dll

Aşağıdaki kod, buradaSystemInfo.cs bulunan System.Web kaynağından aşağı yukarı geliyor :

[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
Bunu denedim, ancak mantıksal işlemci sayısını döndürür - bu da Environment.ProcessorCount çağrılmasıyla aynı sonuçtur.
Bob Bryan

1

Bir seçenek, verileri kayıt defterinden okumak olabilir. Konu Üzerine MSDN Makalesi: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

İşlemcilerin, burada bulunabileceğine inanıyorum, 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();
    }

Kayıt defteri girdisinin çoğu sistemde bulunacağından eminim.

Ben 0.02 $ benim atmak rağmen.


Bu, zaten Environment.ProcessorCount içinde bulunan işlemci sayısını verecektir, her işlemci için çekirdek sayısı elde etmek için başka benzer bir yol var mı?
Armen

0

Aşağıdaki program bir windows makinesinin mantıksal ve fiziksel çekirdeklerini yazdırır.

#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
Bu soru .NET olarak etiketlenmiştir; kodunuz .NET kodu değil.
Wai Ha Lee

-1

Aynı şeyi arıyordum ama herhangi bir nuget veya servis paketi kurmak istemiyorum, bu yüzden bu çözümü buldum, bu tartışmayı kullanarak oldukça basit ve basit, bu WMIC komutunu çalıştırmanın çok kolay olacağını düşündüm ve bu değeri al, işte C # kodu. Yalnızca System.Management ad alanını (ve işlem vb. İçin birkaç standart ad alanını) kullanmanız gerekir.

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
Basit bir WMI sorgusunu neden bu kadar karmaşık yaptığınızdan emin değilsiniz. WMI komut satırını harici bir işlem olarak başlatmak ve çıktısını ayrıştırmak gerçekten gerekli değildir. .NET, WMI sorguları (System.Management.ManagementObjectSearcher) için yerleşik desteğe sahiptir. Ayrıca, neden wmic.exe yerine .NET'in yerleşik WMI desteğini kullanırken nuget paketleri veya hizmet paketleri gerektiğini düşündüğünüzü bilmiyorum ...
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.