วิธีค้นหาจำนวนคอร์ CPU ผ่าน. NET / C #


317

มีวิธีผ่าน. NET / C #เพื่อหาจำนวนแกน CPU หรือไม่

ป.ล. คำถามนี้เป็นคำถามแบบตรงไม่ใช่ "ฉันควรใช้มัลติเธรดหรือไม่" คำถาม! :-)


7
คุณจำเป็นต้องรู้ว่ามีหลายคอร์หรือมีตัวประมวลผลเชิงตรรกะกี่ตัว? สำหรับการรันหลายเธรดอาจเพียงพอ แต่มีสถานการณ์ที่ความแตกต่างอาจมีความสำคัญ
Kevin Kibler

มีวิธีที่ใหม่กว่าในการทำเช่นนี้?
MoonKnight

คำตอบ:


477

มีข้อมูลหลายส่วนที่เกี่ยวข้องกับโปรเซสเซอร์ที่คุณสามารถรับ:

  1. จำนวนตัวประมวลผลทางกายภาพ
  2. จำนวนแกน
  3. จำนวนตัวประมวลผลเชิงตรรกะ

สิ่งเหล่านี้อาจแตกต่างกันไป ในกรณีของเครื่องที่มีโพรเซสเซอร์ที่เปิดใช้งานไฮเปอร์เธรด 2 เธรดมีตัวประมวลผลทางกายภาพ 2 ตัว, 4 คอร์และ 8 ตัวประมวลผลเชิงตรรกะ

จำนวนตัวประมวลผลเชิงตรรกะมีอยู่ในคลาสEnvironmentแต่ข้อมูลอื่นจะมีให้ผ่านWMIเท่านั้น(และคุณอาจต้องติดตั้งHotfix หรือ Service Packเพื่อให้ได้ในบางระบบ):

ตรวจสอบให้แน่ใจว่าได้เพิ่มการอ้างอิงในโครงการของคุณไปยัง System.Management.dll ใน. NET Core นี้จะพร้อมใช้งาน (สำหรับ Windows เท่านั้น) เป็นแพคเกจ NuGet

โปรเซสเซอร์ทางกายภาพ:

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

แกน:

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

หน่วยประมวลผลเชิงตรรกะ:

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

หรือ

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

โปรเซสเซอร์ที่ไม่รวมอยู่ใน Windows:

คุณยังสามารถใช้การเรียก Windows API ในsetupapi.dllเพื่อค้นหาโปรเซสเซอร์ที่แยกออกจาก Windows (เช่นผ่านการตั้งค่าการบูต) และไม่สามารถตรวจพบได้โดยใช้วิธีการข้างต้น รหัสด้านล่างแสดงจำนวนตัวประมวลผลเชิงตรรกะทั้งหมด (ฉันไม่สามารถหาวิธีแยกความแตกต่างทางกายภาพจากตัวประมวลผลเชิงตรรกะ) ที่มีอยู่รวมถึงตัวที่ถูกแยกออกจาก 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: จริง แต่ฉันหวังว่ามันจะอยู่ในรูปแบบที่ดีกว่า การค้นพบค่อนข้างต่ำเมื่อคุณต้องสร้างคิวรีสตริงแบบดิบ
Kevin Kibler

5
WMI Code Creator จะช่วยในการค้นหาค่าและการสร้างคิวรี (มันสามารถสร้างสตับใน c # / vb.net)
StingyJack

4
มันอยู่ใน System.Management.dll คุณรวมการอ้างอิงถึงชุดประกอบนั้นในโครงการของคุณหรือไม่?
Kevin Kibler

2
ปัญหาแบบแยกย่อยในรหัสด้านบน เนื่องจากdeviceCountเป็นพื้นฐานที่เป็นศูนย์การนับหลักควรเป็นเช่นนี้:Console.WriteLine("Number of cores: {0}", deviceCount + 1);
ฟรานซิส Litterio

2
คุณไม่ได้ก่อให้เกิดปัญหาโดยไม่ทิ้งการจัดการวัตถุและผู้ค้นหา?
เบนจามิน

205
Environment.ProcessorCount

[เอกสาร]


12
มันเรียบง่ายมากฉันเกือบจะน้ำตาไหล ขอบคุณสำหรับคำตอบ!
MrGreggles

70
สิ่งนี้จะให้จำนวนของตัวประมวลผลเชิงตรรกะไม่ใช่จำนวนของแกนประมวลผล
Kevin Kibler

8
@KevinKibler จากคำถามฉันสงสัยว่า OP ไม่เข้าใจความแตกต่างและถ้าคุณไม่รู้ความแตกต่างนี่อาจเป็นสิ่งที่คุณต้องการ
เกล็นเมย์นาร์ด

1
สิ่งนี้จะส่งกลับจำนวนที่ผิดในระบบหลักหลายระบบ ฉันใช้หน่วยประมวลผลหลัก dodeca สองตัวพร้อมด้วย hyper-threading ซึ่งทำให้ฉันมีจำนวนตัวประมวลผลเชิงตรรกะ 48 รายการ Environment.ProcessorCountผลตอบแทน 32.
อัลเลนคลาร์กโคปแลนด์จูเนียร์

1
@AlexanderMorou ใช่สิ่งนี้จะล้มเหลวในการให้ผลลัพธ์ที่แม่นยำในเซิร์ฟเวอร์ซีพียูหลายตัว มีการแก้ไขสำหรับสิ่งนี้ แต่ยังไม่ได้ทดสอบเลย
TheLegendaryCopyCoder

35

แบบสอบถาม WMI ช้าดังนั้นลองเลือกเฉพาะสมาชิกที่ต้องการแทนที่จะใช้ Select *

แบบสอบถามต่อไปนี้ใช้เวลา 3.4 วินาที:

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

ในขณะนี้อันนี้ใช้เวลา 0.122 วินาที:

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

1
คุณใช้ระบบอะไรอยู่ ผมใช้หลายรายการ "เลือก *" คำสั่งและจะไม่ใช้เวลาใดก็ได้ที่อยู่ใกล้กับ 3.4 วินาทีการทดสอบในพันของคอมพิวเตอร์ที่ซอฟแวร์ของฉันคือการใช้งานบน ฉันเลือก * เนื่องจากฉันได้รับคุณสมบัติหลายอย่างจากวัตถุ อย่างไรก็ตามฉันทำมันแตกต่างกันเล็กน้อย: สร้าง ObjectQuery บน Select *; รับ ManagementObjectCollection จากนั้น foreach ManagementObject ใน ManagementObjectCollection
deegee

@deegee: คุณพูดถูกแบบสอบถามไม่ได้ใช้เวลานานกว่าในการ "เลือก *" มันเป็นเพียงว่า int การแยกวิเคราะห์ด้านล่างจะช้าถ้าทำซ้ำค่าทั้งหมดที่ส่งกลับแทน NumberOfCores
Aleix Mercader


10

มันค่อนข้างน่าสนใจที่จะเห็นว่า. NET ทำให้สิ่งนี้ภายในพูดได้น้อยที่สุด ... มันเป็น "ง่าย" ดังต่อไปนี้:

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

6

วิธีที่ง่ายที่สุด = Environment.ProcessorCount
Exemple จากคุณสมบัติ Environment.ProcessorCount

using System;

class Sample 
{
    public static void Main() 
    {
        Console.WriteLine("The number of processors " +
            "on this computer is {0}.", 
            Environment.ProcessorCount);
    }
}

บางครั้งเมธอด Environment.ProcessorCount จะแสดงข้อมูลที่ไม่ถูกต้อง (ดูstackoverflow.com/questions/27965962/… )
สร้าง

4

จาก. NET Framework ต้นทาง

คุณสามารถรับมันได้ด้วยPInvokeเปิดKernel32.dll

รหัสต่อไปนี้มามากหรือน้อยจากSystemInfo.csแหล่ง System.Web อยู่ที่นี่ :

[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
พยายามทำสิ่งนี้ แต่จะคืนค่าจำนวนตัวประมวลผลเชิงตรรกะ - ซึ่งเป็นผลลัพธ์เดียวกันกับการเรียก Environment.ProcessorCount
Bob Bryan

1

ทางเลือกหนึ่งคือการอ่านข้อมูลจากรีจิสทรี บทความ MSDN ในหัวข้อ: http://msdn.microsoft.com/en-us/library/microsoft.win32.registry.localmachine(v=vs.71).aspx )

โปรเซสเซอร์ฉันเชื่อว่าสามารถอยู่ที่นี่ 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();
    }

ฉันแน่ใจว่ารายการรีจิสทรีจะมีในระบบส่วนใหญ่

แม้ว่าฉันจะโยน $ 0.02 ของฉันมา


สิ่งนี้จะให้จำนวนโปรเซสเซอร์ที่มีอยู่แล้วใน Environment.ProcessorCount มีวิธีอื่นใดที่คล้ายกันในการรับจำนวนแกนประมวลผลสำหรับโปรเซสเซอร์แต่ละตัวหรือไม่
อาร์เมน

0

โปรแกรมต่อไปนี้พิมพ์แกนตรรกะและฟิสิคัลของเครื่อง 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
คำถามนี้ติดแท็ก. NET รหัสของคุณไม่ใช่รหัส. NET
Wai Ha Lee

-1

ฉันกำลังมองหาสิ่งเดียวกัน แต่ฉันไม่ต้องการติดตั้ง nuget หรือ servicepack ใด ๆ ดังนั้นฉันจึงพบวิธีแก้ปัญหานี้มันค่อนข้างง่ายและตรงไปตรงมาโดยใช้การสนทนานี้ฉันคิดว่ามันจะง่ายต่อการเรียกใช้คำสั่ง WMIC และรับค่านั่นคือรหัส C # คุณจะต้องใช้เนมสเปซ System.Management (และเพิ่มเนมสเปซมาตรฐานสองเท่าสำหรับกระบวนการและอื่น ๆ )

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
ไม่แน่ใจว่าทำไมคุณทำแบบสอบถาม WMI ง่าย ๆ ที่ซับซ้อน การสตาร์ทบรรทัดคำสั่ง WMI เป็นกระบวนการภายนอกและการแยกวิเคราะห์เอาต์พุตไม่จำเป็นจริงๆ .NET มีการสนับสนุนการสืบค้น WMI ในตัว (System.Management.ManagementObjectSearcher) ซึ่งเป็นคำตอบอื่น ๆ ที่แสดงไว้ที่นี่แล้ว นอกจากนี้ฉันไม่ทราบสาเหตุที่คุณคิดว่าแพคเกจ nuget หรือเซอร์วิสแพ็คเป็นสิ่งจำเป็นเมื่อใช้การสนับสนุน WMI ในตัวของ. NET แทน wmic.exe ...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.