【问题标题】:How to detect number of logical and physical processors efficiently?如何有效地检测逻辑和物理处理器的数量?
【发布时间】:2023-03-12 07:20:01
【问题描述】:

目前我正在使用此功能。它工作正常,但每个查询大约需要 1 秒。所以在我的情况下,我在我的应用程序中浪费了 3 秒。目前我正在考虑使用 3 个线程在一秒钟内获取所有信息。

function GetWMIstring (wmiHost, wmiClass, wmiProperty : string):string;
var  // These are all needed for the WMI querying process
  Locator:  ISWbemLocator;
  Services: ISWbemServices;
  SObject:  ISWbemObject;
  ObjSet:   ISWbemObjectSet;
  SProp:    ISWbemProperty;
  Enum:     IEnumVariant;
  Value:    Cardinal;
  TempObj:  OleVariant;
  SN: string;
begin
  Result := '';
  try
  Locator := CoSWbemLocator.Create;  // Create the Location object
  // Connect to the WMI service, with the root\cimv2 namespace
  Services := Locator.ConnectServer(wmiHost, 'root\cimv2', '', '', '','', 0, nil);
  ObjSet := Services.ExecQuery('SELECT * FROM '+wmiClass, 'WQL',
    wbemFlagReturnImmediately and wbemFlagForwardOnly , nil);
  Enum := (ObjSet._NewEnum) as IEnumVariant;
  while Enum.Next(1, TempObj, Value) = S_OK do
  begin
    try SObject := IUnknown(TempObj) as ISWBemObject; except SObject := nil; end;
    TempObj := Unassigned;  // Always need to free interface in TempObj
    if SObject <> nil then
    begin
      SProp := SObject.Properties_.Item(wmiProperty, 0);
      SN := SProp.Get_Value;
      if not VarIsNull(SN) then
      begin
        Result :=  SN;
        Break;
      end;
    end;
  end;
  except // Trap any exceptions (Not having WMI installed will cause one!)
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin

  CoInitializeEx(nil,COINIT_MULTITHREADED) ; //required !!! Otherwise "EOleSysError: CoInitialize has not been called" occurs   (Microsoft recommends CoInitializeEx instead of CoInitialize)

  CPU_PROCESSOR_COUNT := StrToInt( getWMIstring('','Win32_ComputerSystem','NumberOfProcessors') );   // number of cpu on mainboard
  CPU_PHYSICAL_CORES := StrToInt( getWMIstring('','Win32_Processor','NumberOfCores') );      // number of phisical cores
  CPU_LOGICAL_CORES := StrToInt( getWMIstring('','Win32_Processor','NumberOfLogicalProcessors') );     //number of logical cores

  CoUninitialize; //required !!!

end;

【问题讨论】:

  • 添加更多线程是错误的解决方案。请改用GetLogicalProcessorInformationGetLogicalProcessorInformationEx。此外,您对CoInitializeEx 的呼叫非常可疑。默认情况下,VCL 应用程序在主线程中初始化 COM。你做了什么来打破它。此外,您不能只初始化 COM,并在线程生命周期中的任意时间点完成它。这势必会引起冲突。无论如何,一旦您删除了 WMI,您就不必担心这一点,但您似乎在其他地方破坏了 COM 初始化。
  • 我会使用this
  • @DavidHeffernan 从上下文中我猜想 OP 正在从线程执行Button1Click(因此出现 COM 异常)。当然,这绝对不是正确的处理方式。
  • @J... 很难想象会是这样。框架在主线程中触发按钮点击
  • @DavidHeffernan:我在哪里建议 WMI? RRUZ 的 uSMBIOS 单元使用 WinApi(如果您定义了标志,还使用 ​​WMI)?

标签: delphi delphi-7


【解决方案1】:

这是使用GetLogicalProcessorInformation 的示例:

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

type
  TLogicalProcessorInformation = record
    LogicalProcessorCount : integer;
    NumaNodeCount : integer;
    ProcessorCoreCount : integer;
    ProcessorL1CacheCount : integer;
    ProcessorL2CacheCount : integer;
    ProcessorL3CacheCount : integer;
    ProcessorPackageCount : integer;
  end;

function CountSetBits(bitMask : NativeUInt) : integer;
var
  lShift, i : integer;
  bitTest : NativeUInt;
begin
  lShift := SizeOf(NativeUInt)*8 - 1;
  result := 0;
  bitTest := 1 shl lShift;    
  for i := 0 to lShift do begin
    if (bitMask and bitTest) <> 0 then Inc(result);
    bitTest := bitTest shr 1;
  end;
end;

function GetLogicalProcessorInfo : TLogicalProcessorInformation;
var
  i: Integer;
  ReturnLength: DWORD;
  Buffer: array of TSystemLogicalProcessorInformation;
begin
  result.LogicalProcessorCount := 0;
  result.NumaNodeCount := 0;
  result.ProcessorCoreCount := 0;
  result.ProcessorL1CacheCount := 0;
  result.ProcessorL2CacheCount := 0;
  result.ProcessorL3CacheCount := 0;
  result.ProcessorPackageCount := 0;
  SetLength(Buffer, 256);
  if not GetLogicalProcessorInformation(@Buffer[0], ReturnLength) then
  begin
    if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin
      SetLength(Buffer,
        ReturnLength div SizeOf(TSystemLogicalProcessorInformation) + 1);
      if not GetLogicalProcessorInformation(@Buffer[0], ReturnLength) then
        RaiseLastOSError;
    end else
      RaiseLastOSError;
  end;
  SetLength(Buffer, ReturnLength div SizeOf(TSystemLogicalProcessorInformation));

  for i := 0 to High(Buffer) do begin
    case Buffer[i].Relationship of
        RelationNumaNode: Inc(result.NumaNodeCount);
        RelationProcessorCore:
          begin
            Inc(result.ProcessorCoreCount);
            result.LogicalProcessorCount := result.LogicalProcessorCount + CountSetBits(Buffer[i].ProcessorMask);
          end;
        RelationCache:
          begin
            if (Buffer[i].Cache.Level = 1) then Inc(result.ProcessorL1CacheCount)
            else if (Buffer[i].Cache.Level = 2) then Inc(result.ProcessorL2CacheCount)
            else if (Buffer[i].Cache.Level = 3) then Inc(result.ProcessorL3CacheCount);
          end;
        RelationProcessorPackage: Inc(result.ProcessorPackageCount);
        else
          raise Exception.Create('Error: Unsupported LOGICAL_PROCESSOR_RELATIONSHIP value.');
    end;
  end;
end;

var
  LProcInfo : TLogicalProcessorInformation;
begin
  LProcInfo := GetLogicalProcessorInfo;
  WriteLn('Logical processor count = ', LProcInfo.LogicalProcessorCount);
  WriteLn('NUMA Node count = ', LProcInfo.NumaNodeCount);
  WriteLn('Processor Core count = ', LProcInfo.ProcessorCoreCount);
  WriteLn('L1 Cache count = ', LProcInfo.ProcessorL1CacheCount);
  WriteLn('L2 Cache count = ', LProcInfo.ProcessorL2CacheCount);
  WriteLn('L3 Cache count = ', LProcInfo.ProcessorL3CacheCount);
  WriteLn('Package count = ', LProcInfo.ProcessorPackageCount);
  ReadLn;
end.

对于 XE2 及更高版本,这些 WinAPI 定义包含在 RTL 中。否则可以手动包含定义。一个示例实现can be found here

例如,对于桌面 i7 工作站,此输出:

逻辑处理器数 = 8
NUMA 节点数 = 1
处理器核心数 = 4
L1 缓存计数 = 8
L2 缓存计数 = 4
L3 缓存计数 = 1
包数 = 1

逻辑内核数包括超线程虚拟内核数,处理器内核数是物理内核数,包数返回物理 CPU 数,NUMA 节点数返回处理器节点数(对于大型集群计算机)。

如果您希望它在更大的集群上运行,请注意,请参阅文档:

在具有超过 64 个逻辑处理器的系统上,GetLogicalProcessorInformation 函数检索有关当前分配调用线程的处理器组中处理器的逻辑处理器信息。使用 GetLogicalProcessorInformationEx 函数检索有关系统上所有处理器组中的处理器的信息。

【讨论】:

  • 我的 Delphi 7 无法从您的链接中识别 ULONG_PTR 和 ULONGLONG 类型。处理器掩码:ULONG_PTR; 3:(保留:ULONGLONG的数组[0..1]);
  • @Atak_Snajpera 我已在您的问题中添加了delphi-7 标签 - 这始终是有用的信息。否则我们必须猜测您使用的是哪个版本。 ULONG_PTR 您可以简单地更改为CardinalULONGLONGUint64
  • UInt64 在 D7 中不可用,但我相信 Int64 在这种情况下也会做得很好。
  • @KenBourassa 我认为这是相反的方式(UInt64 但没有 Int64 ...)。不幸的是,手头没有 D7。
  • @J...,没关系,我读我的参考书有点太快了... UInt64 在 Delphi 7 中可用,但它只是 Int64 的别名(它已签名)。 UInt64 仅在 D2007 开始变得无符号。 stackoverflow.com/questions/36070384/…
【解决方案2】:

如果您对逻辑处理器的数量感兴趣 - 比如说出于线程调度目的,有更简单和更早的 GetSystemInfo 函数:

function GetNumberOfProcessors: Integer;
var
   si: TSystemInfo; //Windows.pas
begin
   GetSystemInfo({var}si);
   Result := si.dwNumberOfProcessors;
end;

几乎所有用途:

  • 它们是否是单独的物理 CPU 无关紧要
  • 或者如果它们是核心
  • 或多个物理 CPU,每个 CPU 都有多个内核
  • 或者如果它们是超线程虚拟内核

您想要 GetSystemInfo 提供的功能。

【讨论】:

  • 在 Delphi 中你甚至不需要调用这个函数,因为 RTL 在初始化时会为你做这些。您可以简单地读取全局 System.CPUCount 变量。 (由System 单元的初始化部分设置。)
  • 我假设 CPUCount 是在 Delphi 5 之后的某个时间添加的。
  • TSytemInfo 中的拼写错误;应该是 TSystemInfo;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-23
  • 1970-01-01
  • 1970-01-01
  • 2019-12-14
  • 2012-11-15
  • 2010-10-05
相关资源
最近更新 更多