Thema: Delphi Level 2 Cache

Einzelnen Beitrag anzeigen

Muetze1
(Gast)

n/a Beiträge
 
#9

Re: Level 2 Cache

  Alt 20. Apr 2004, 22:32
Moin!

Mensch, manchmal wird man auch betriebsblind... Die If Bedingung ist einfach falsch herum gestellt (Wenn mehr als 1000 KB, dann zeige in KB an, ansonsten in MB)...

Ok, ich ändere den Code mal schnell...

@other: Falls ihr den Code schon genutzt habt, If Bedingung 2x ändern (ja ja Copy&Paste) oder nochmals neu kopieren und einfügen...

/EDIT: Ok, ändern geht nicht mehr in diesem Forum und dafür den Mod belästigen muss nicht sein, daher jetzt der funktionierende Code...

Delphi-Quellcode:
Unit L2Cache;

Interface

  Function GetL2CacheSize : String;

Implementation

uses
  sysutils;

  // prüfen, ob die CPU die CPUID Instruktion unterstützt
  //
  // Anmerkung: Bei Cyrix M2 CPU's muss man die Instruktion vorher in einem
  // Cyrix spezifischen Register enablen, sonst ist sie unbekannt
  // und die EFlags verhalten sich dem entsprechend. In diesem Falle
  // ist das aber hinfällig, da Cyrix CPU's keine Cache Informationen
  // über CPUID zurückliefern sondern nur über ihre Configuration
  // Register auf Port $FE und $FF...
Function CheckForCPUID : Boolean; Assembler;
Asm
  push ebx
  push ecx
  pushfd // save EFLAGS
  pop eax // store EFLAGS in EAX
  mov ebx, eax // save in EBX for later testing
  xor eax, 00200000h // toggle bit 21
  push eax // put to stack
  popfd // save changed EAX to ELFAGS
  pushfd // push EFLAGS on stack
  pop ecx // store EFLAGS in EAX
  mov Result, False
  cmp ecx, ebx // see if bit 21 has changed
  jz @@no_CPUID // if no change, no CPUID
  mov Result, True // set flag
@@no_CPUID:
  pop ecx
  pop ebx
End;

Function GetL2CacheSize : String;
Var
  CPUID_VendorID : Array[0..11] Of Char; // für den Vendor-String
  MaxStdLevel, // höchstes CPUID Standardlevel
  MaxExtLevel : LongWord; // höchstes CPUID erweitertes Level

    // Unterfunktion für Intel CPU's
  Function GetIntelCacheInfo : String;
  Var
    CallCtr : Byte; // gibt an, wie oft CPUID 02h aufgerufen
                                         // werden muss, um alle Cache Info's zu
                                         // bekommen
    Regs : array[0..3] Of LongWord; // Speicher für die Register nach CPUID 02h
    i : Integer; // Zählvariable
    KBSize : Word; // die Grösse des L2 Caches in KByte

      // die Intel CPU's geben in mehreren Byte Werten ihre Info's an, und
      // es kann mehrere ID's für den selben Cache Typ geben, daher hier anhand
      // der ID die Grösse zurückgeben oder 0, wenn es kein L2 Cache Deskriptor war
    Function CacheInfo(CacheID : Byte): Word;
    Begin
      Case CacheID Of
        $39, $3B,
        $41, $79 : Result := 128;

        $3C, $42,
        $7A, $82 : Result := 256;

        $43, $7B,
        $83, $86 : Result := 512;

        $44, $7C,
        $84, $87 : Result := 1024;

        $85, $45 : Result := 2048;
      Else
        Result := 0;
      End;
    End;

    // ok, ans eingemachte für die Intel Cache Info's
  Begin
    Asm
      push eax // register sichern - alle - auch wenn in der Delphi Hilfe
      push ebx // steht, das man einzelne Register frei verändern darf.
      push ecx // Diese Aussage trifft nur bei nicht optimierte Codes zu, wenn
      push edx // die Optimierung an ist (default), dann fällt Delphi dadurch
                // gerne auf die Schnauze...

      mov eax, 2 // Funktion laden (hier: 0000.0002h)
      db 00fh, 0a2h // CPUID

      mov CallCtr, al // CallCtr merken
      mov Regs[0].longword, eax // Register sichern zum auswerten
      mov Regs[1].longword, ebx
      mov Regs[2].longword, ecx
      mov Regs[3].longword, edx

      pop edx // Register wiederherstellen, damit Delphi keine Problem bekommt
      pop ecx
      pop ebx
      pop eax
    End;

    // CallCtr lass ich mal aussen vor, wird bisher noch nicht benutzt von Intel
    // (also noch nicht > 1)

    // ok, dann zählen wir mal die KBytes zusammen der einzelnen Deskriptoren
    KBSize := 0;
    If ( Regs[0] And $80000000 ) = 0 Then // prüfen ob gültige Angabe
    Begin
      If ( ( Regs[0] Shr 8 ) And $ff ) > 0 Then // wenn was drinne steht ...
        Inc(KBSize, CacheInfo(( Regs[0] Shr 8 ) And $ff)); // ... dann auswerten
      If ( ( Regs[0] Shr 16 ) And $ff ) > 0 Then
        Inc(KBSize, CacheInfo(( Regs[0] Shr 16 ) And $ff));
      If ( ( Regs[0] Shr 24 ) And $ff ) > 0 Then
        Inc(KBSize, CacheInfo(( Regs[0] Shr 24 ) And $ff));
    End;

    // da in EAX der CallCtr mit drinne steht, wird das hier drüber extra behandelt
    // und die restlichen können wir in einem Rutsch durchgehen
    For i := 1 To 3 Do
    Begin
      If ( Regs[i] And $80000000 ) = 0 Then
      Begin
        If ( ( Regs[i] Shr 8 ) And $ff ) > 0 Then
          Inc(KBSize, CacheInfo(( Regs[i] Shr 8 ) And $ff));
        If ( ( Regs[i] Shr 16 ) And $ff ) > 0 Then
          Inc(KBSize, CacheInfo(( Regs[i] Shr 16 ) And $ff));
        If ( ( Regs[i] Shr 24 ) And $ff ) > 0 Then
          Inc(KBSize, CacheInfo(( Regs[i] Shr 24 ) And $ff));
      End;
    End;

    // Ausgabe bilden
    If ( KBSize < 1024 ) Then // noch im KByte Bereich?
      Result := IntToStr(KBSize) + ' KB'    // ok, dann KByte
    Else
      Result := IntToStr(KBSize Div 1024) + ' MB'; // nein, also MByte
  End;

  // ok, gleiches für AMD
  Function GetAMDCacheInfo : String;
  Var
    KBSize : Word;
  Begin
    // AMD macht es uns einfacher, die haben direkt eine Angabe des L1 und L2
    // Cache-Grösse in KByte als Rückgabewert

    Asm
      push eax // siehe oben
      push ebx
      push ecx
      push edx

      mov eax, 080000006h // Funktionsnummer laden (hier: 8000.0006h)
      db 00fh, 0a2h // CPUID

      shr ecx, 16 // in Bits 16..31 steht die Grösse in KByte
      mov KBSize, cx // daher: runterschieben und die 16 Bits merken

      pop edx
      pop ecx
      pop ebx
      pop eax
    End;

    // Ausgabe bilden - same procedure as every cpu... ;-)
    If ( KBSize < 1024 ) Then
      Result := IntToStr(KBSize) + ' KB'
    Else
      Result := IntToStr(KBSize Div 1024) + ' MB';
  End;

Begin
  // ok, hier gehts los

  If ( CheckForCPUID ) Then // haben wir überhaupt eine Chance, oder anders:
  Begin // bietet die CPU einen CPUID Befehl?
    Asm
      push eax // wieder Register sichern...
      push ebx
      push ecx
      push edx

      // xor eax, eax setzt eax auf 0 - laut AMD Optimizing Doc ist mov eax, 0
      // schneller beim AMD. Laut Intel ist xor eax, eax schneller bei Intel...

      xor eax, eax // Funktionsnummer laden (hier: 0000.0000h)
      db 00fh, 0a2h // CPUID

      // in EAX steht das höchste Standard CPUID Level -> merken
      mov MaxStdLevel, eax

      // wir wollen den Vendor String speichern - und das geht am einfachsten
      // mit den String Befehlen der CPU...
      push edi // enhanced destination index = Zieladresse
                                // vorher aber erstmal sichern...
      lea edi, CPUID_VendorID // dann die Adresse der Variablen holen
      cld // direction flag löschen, wir wollen EDI erhöhen...
      mov eax, ebx // als erstes muss der Inhalt von EBX gespeichert
                                // werden, und da SToreStringDword immer EAX
                                // schreibt, muss EBX in EAX gepackt werden...
      stosd // ok, schreiben
      mov eax, edx // gleiches mit EDX
      stosd // und hinten ranhängen
                                // (EDI ist jetzt schon auf CPUID_VendorID[4])
      mov eax, ecx // gleiches mit ECX
      stosd
      pop edi // ok, Inhalt von EDI wiederherstellen

      // auf erweiterten CPUID testen
      mov eax, 080000000h // Funktionsnummer laden (hier: 8000.0000h)
      db 00fh, 0a2h // CPUID
      mov MaxExtLevel, eax // maximale Funktionsnummer sichern...

      pop edx // Register wiederherstellen
      pop ecx
      pop ebx
      pop eax
    End;

    // nun gehts an die Auswertung und da kocht jeder Hersteller sein eigenes
    // Süppchen...
    If ( String(CPUID_VendorID) = 'GenuineIntel' ) Then // Intel
    Begin
      If ( MaxStdLevel >= 2 ) Then // wir brauchen min. CPUID 0000.0002h
        Result := GetIntelCacheInfo
      Else
        Result := 'unkown';
    End
    Else If ( String(CPUID_VendorID) = 'AuthenticAMD' ) Then // AMD
    Begin
      // wir brauchen min. CPUID 8000.0006h
      If ( MaxExtLevel >= $80000006 ) Then // ab K6-III
        Result := GetAMDCacheInfo
      Else
        Result := 'unkown';
    End
    Else If ( String(CPUID_VendorID) = 'CyrixInstead' ) Then
    Begin
      // tja, die Cyrix'e - keine Cache Info im CPUID, alles per Cyrix
      // specific Register...
      Result := 'no cache info';
    End;

    // Transmeta und weitere erstmal wegfallen lassen...
  End
  Else
    Result := 'no CPUID support';
End;

End.
Ich habe den Code gleich mal ein wenig kommentiert, damit kommt vielleicht ein wenig Klarheit auf über das was ich da überhaupt mache...

MfG
Muetze1
  Mit Zitat antworten Zitat