I've tried to get CPU cache information by using WMI and it works great, but only for Level 2 and Level 3 cache, so my question is, how to get CPU Level 1 cache information ?
Asked
Active
Viewed 2,026 times
3
-
3There's a native API for this: `GetLogicalProcessorInformation` and `GetLogicalProcessorInformationEx`. WMI seems overkill. – David Heffernan Dec 09 '12 at 15:27
2 Answers
9
Here's the WinAPI way, which uses the GetLogicalProcessorInformation
function. If you have at least Delphi XE2, you won't need the following definitions, they are already in the Windows
unit:
type
TLogicalProcessorRelationship = (
RelationProcessorCore = 0,
RelationNumaNode = 1,
RelationCache = 2,
RelationProcessorPackage = 3,
RelationGroup = 4,
RelationAll = $FFFF
);
TProcessorCacheType = (
CacheUnified,
CacheInstruction,
CacheData,
CacheTrace
);
TCacheDescriptor = record
Level: Byte;
Associativity: Byte;
LineSize: Word;
Size: DWORD;
pcType: TProcessorCacheType;
end;
PSystemLogicalProcessorInformation = ^TSystemLogicalProcessorInformation;
TSystemLogicalProcessorInformation = record
ProcessorMask: ULONG_PTR;
Relationship: TLogicalProcessorRelationship;
case Integer of
0: (Flags: Byte);
1: (NodeNumber: DWORD);
2: (Cache: TCacheDescriptor);
3: (Reserved: array [0..1] of ULONGLONG);
end;
function GetLogicalProcessorInformation(
Buffer: PSystemLogicalProcessorInformation;
var ReturnLength: DWORD): BOOL; stdcall;
external kernel32 name 'GetLogicalProcessorInformation';
And the example of how to show the cache type, level and size for all level 1 cache entries:
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
I: Integer;
ReturnLength: DWORD;
Buffer: array of TSystemLogicalProcessorInformation;
begin
SetLength(Buffer, 1);
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;
for I := 0 to High(Buffer) do
begin
if (Buffer[I].Relationship = RelationCache) and
(Buffer[I].Cache.Level = 1) then
begin
S := 'Type: ';
case Buffer[I].Cache.pcType of
CacheUnified: S := S + 'Unified cache';
CacheInstruction: S := S + 'Instruction cache';
CacheData: S := S + 'Data cache';
CacheTrace: S := S + 'Trace cache';
end;
S := S + sLineBreak;
S := S + 'Level: ' + IntToStr(Buffer[I].Cache.Level) + sLineBreak;
S := S + 'Size: ' + IntToStr(Buffer[I].Cache.Size) + ' B';
ShowMessage(S);
end;
end;
end;

TLama
- 75,147
- 17
- 214
- 392
-
1+1 This is the right way to do it from Delphi. Note that Windows.pas defines GetLogicalProcessorInformation and the various types needed. At least it does in XE3. I'm not sure when it was added. – David Heffernan Dec 09 '12 at 16:32
-
-
-
2@Remko, that's what I usually do, but I always check their translations against the reference. At this time there's a wrong definition in [`JwaWinNT.pas`](http://jedi-apilib.svn.sourceforge.net/viewvc/jedi-apilib/jwapi/trunk/Win32API/JwaWinNT.pas?view=markup) in the `_SYSTEM_LOGICAL_PROCESSOR_INFORMATION` record at the union part, there's no `Cache` member, which is the most wanted for this case. – TLama Dec 09 '12 at 17:45
-
2
-
@Remko, yes, but I'm sooo lazy these days and I knew you're the one who can take care of it :-) – TLama Dec 09 '12 at 21:19
-
@TLama I get an error: First chance exception at $00464CCD. Exception class $C0000005 with message 'access violation at 0x00464ccd: read of address 0x00001158'. Process Project1.exe (5088) – Giacomo King Patermo Dec 10 '12 at 18:28
5
You can use the Win32_CacheMemory
WMI class, try this code:
{$APPTYPE CONSOLE}
uses
SysUtils,
ActiveX,
ComObj,
Variants;
procedure GetWin32_CacheMemoryInfo;
const
WbemUser ='';
WbemPassword ='';
WbemComputer ='localhost';
wbemFlagForwardOnly = $00000020;
var
FSWbemLocator : OLEVariant;
FWMIService : OLEVariant;
FWbemObjectSet: OLEVariant;
FWbemObject : OLEVariant;
oEnum : IEnumvariant;
iValue : LongWord;
begin;
FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
FWMIService := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
FWbemObjectSet:= FWMIService.ExecQuery('SELECT MaxCacheSize, Purpose FROM Win32_CacheMemory','WQL',wbemFlagForwardOnly);
oEnum := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
while oEnum.Next(1, FWbemObject, iValue) = 0 do
begin
Writeln(Format('MaxCacheSize %d',[Integer(FWbemObject.MaxCacheSize)]));
Writeln(Format('Purpose %s',[String(FWbemObject.Purpose)]));
FWbemObject:=Unassigned;
end;
end;
begin
try
CoInitialize(nil);
try
GetWin32_CacheMemoryInfo;
finally
CoUninitialize;
end;
except
on E:EOleException do
Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
on E:Exception do
Writeln(E.Classname, ':', E.Message);
end;
Writeln('Press Enter to exit');
Readln;
end.

RRUZ
- 134,889
- 20
- 356
- 483
-
Thanks it works, I used GetWin32_ProcessorInfo, but instead I use GetWin32_CacheMemory;, thanks again. – Giacomo King Patermo Dec 09 '12 at 16:18
-
1@RRUZ Is there any reason to prefer this solution to the approach that uses the Win32 API? – David Heffernan Dec 09 '12 at 16:39
-
@RRUZ How to get only LEVEL1? CacheMemoryInfo show all Level cache size. Thanks. – Giacomo King Patermo Dec 09 '12 at 16:55
-
1@GiacomoKingPatermo, for only retrieve the Level 1 Cache try `SELECT MaxCacheSize, Purpose FROM Win32_CacheMemory Where Purpose="L1-Cache"` – RRUZ Dec 09 '12 at 19:38