Dies kann prima auch zur (Geschwindigkeits-)Optimierung des eigenen Codes verwendet werden. Anstelle der doch von vielen Faktoren abhängigen Zeit-Messung kann die Anzahl benötigter CPU-Takte, CPU-Instruktionen und Sprünge (sind immer eine Bremse!) zum Vergleich verschiedener Code-Versionen verwendet werden. Und interessant sind diese Werte allemal!
Für die MSR-Nutzung sind allerdings unter Windows Ring0-Zugriffs-Rechte notwendig (Administrator-Rechte sowieso). Ich verwende dafür die WinRing0-Dll´s (32/64-Bit, http://sourceforge.net/projects/winring0), die Rings "ausgegraben" und auch für Vista64 getestet hat (an dieser Stelle Dank an Rings!).
Hinweise dazu siehe auch http://www.purebasic.fr/german/viewtopi ... 17&t=20728 (Hardware-Ecke). Benutzung auf eigene Gefahr!
Code: Alles auswählen
;- Ermittlung Anzahl der benötigten CPU-Takte, Instruktionen und Sprünge für einen Testcode, ab Intel Core 2
;- "Helle" Klaus Helbing, 22.05.2011
;- Nutzung der WinRing0-Dll´s (32/64-Bit). Copyright (c) 2007-2009 OpenLibSys.org. All rights reserved.
#IA32_PERFEVTSEL0 = $186 ;Unique, d.h. bei Multi-Cores jeder Core eigenes MSR
#IA32_PERF_GLOBAL_CTRL = $38F ;Unique
#IA32_PMC_0 = $C1 ;Unique
#Bit0 = $1
#Bit16 = $10000 ;User Mode = Bit16
#Bit17 = $20000 ;Operating System Mode = Bit17
#Bit22 = $400000 ;Enable Counters = Bit22
#UnHalted_Core_Cycles = $3C ;Event Select Zählung Core-Cycles
#Instructions_Retired = $C0 ;Event Select Zählung Instructions
#Branch_Instructions_Retired = $C4 ;Event Select Zählung ausgeführte Sprünge
#PROCESSOR_ARCHITECTURE_AMD64 = $9
#IA32_TIME_STAMP_COUNTER = $10 ;Unique
Core.l
Hi.l
Lo.l
Kali_Hi.l
Kali_Lo.l
Mess_Hi.l
Mess_Lo.l
TMask.l
Cycles.q
Instructions.q
Jumps.q
;mittels CPUID ermitteln, ob die CPU Performance Monitoring beherrscht
!mov eax,0h ;Ermittlung Largest Standard Function Number
!cpuid
!cmp eax,0Ah
!jae @f
MessageRequester("Abbruch !", "Performance Monitoring wird von dieser CPU nicht unterstützt !")
End
!@@:
!mov eax,0Ah ;Ermittlung Performance Monitor Features
!cpuid
!and ebx,100011b ;Bit0=Core Cycles, Bit1=Instructions, Bit5=Branch Instructions
!jz @f
MessageRequester("Abbruch !", "Es werden nicht alle benötigten Funktionen unterstützt !")
End
!@@:
Procedure InstructionsTest() ;Testcode, hier sehr einfach
For i = 1 To 10000
j + 1
Next
EndProcedure
;ermitteln, ob 32- oder 64-Bit-Betriebssystem
SI.SYSTEM_INFO ;Structure System_Info
GetSystemInfo_(@SI)
If SI\wProcessorArchitecture = #PROCESSOR_ARCHITECTURE_AMD64
OS3264 = 1
EndIf
;die entsprechende DLL öffnen
If OS3264
DLLOK = OpenLibrary(0, "WinRing0x64.dll") ;64-Bit-Version laden
Else
DLLOK = OpenLibrary(0, "WinRing0.dll") ;32-Bit-Version laden
EndIf
If DLLOK
Prototype.i ProtoWinRing0_0()
WR0_InitializeOls.ProtoWinRing0_0 = GetFunction(0, "InitializeOls")
WR0_DeinitializeOls.ProtoWinRing0_0 = GetFunction(0, "DeinitializeOls")
WR0_IsMsr.ProtoWinRing0_0 = GetFunction(0, "IsMsr")
Prototype.i ProtoWinRing0_4(V1l.l, V2l.l, V3l.l, V4l.l)
WR0_RdmsrTx.ProtoWinRing0_4 = GetFunction(0, "RdmsrTx")
WR0_WrmsrTx.ProtoWinRing0_4 = GetFunction(0, "WrmsrTx")
If WR0_InitializeOls()
If WR0_IsMsr()
Core = 1
hThread = GetCurrentThread_()
TMask = SetThreadAffinityMask_(hThread, Core) ;Thread nur von Core0 ausführen lassen (wenn Multi-Core-CPU)
Old_Priority = GetThreadPriority_(hThread)
SetThreadPriority_(hThread, #THREAD_PRIORITY_TIME_CRITICAL)
WR0_RdmsrTx(#IA32_PERF_GLOBAL_CTRL, @Lo, @Hi, Core)
Hi_Old = Hi ;sichern
Lo_Old = Lo
Lo = Lo | #Bit0 ;Counter0 aktivieren
WR0_WrmsrTx(#IA32_PERF_GLOBAL_CTRL, Lo, Hi, Core)
UMode = #UnHalted_Core_Cycles | #Bit16 | #Bit22 ;nur User Mode, hier für 1.Wert
;AMode = #UnHalted_Core_Cycles | #Bit16 | #Bit17 | #Bit22 ;All (User Mode und Operating System Mode)
For k = 1 To 3 ;3 Werte ermitteln
;Kalibrierung, d.h. Ausführung ohne Testcode
WR0_WrmsrTx(#IA32_PERFEVTSEL0, UMode, 0, Core) ;oder AMode für Tests
WR0_WrmsrTx(#IA32_PMC_0, 0, 0, Core) ;auf Null setzen
WR0_RdmsrTx(#IA32_PMC_0, @Kali_Lo, @Kali_Hi, Core) ;Core0 auslesen
WR0_WrmsrTx(#IA32_PMC_0, 0, 0, Core) ;wieder auf Null setzen
;Messung, d.h. Ausführung mit Testcode
WR0_WrmsrTx(#IA32_PERFEVTSEL0, UMode, 0, Core)
WR0_WrmsrTx(#IA32_PMC_0, 0, 0, Core) ;auf Null setzen
;--------------------------------------
;zu testender Code; sollte für Takte-Ermittlung eine gewisse Mindestlänge haben
InstructionsTest() ;oder direkt einfügen
;--------------------------------------
;Werte auslesen
WR0_RdmsrTx(#IA32_PMC_0, @Mess_Lo, @Mess_Hi, Core) ;Core0 auslesen
WR0_WrmsrTx(#IA32_PMC_0, 0, 0, Core) ;wieder auf Null setzen
Select k
Case 1
Cycles = ((Mess_Hi - Kali_Hi) << 32) + Mess_Lo - Kali_Lo ;kleine Differenzen sind systembedingt
C$ = "Anzahl der durchgelaufenen Core-Takte : " + StrU(Cycles, #PB_Quad) + #LFCR$
UMode = #Instructions_Retired | #Bit16 | #Bit22 ;für nächsten Wert
Case 2
Instructions = ((Mess_Hi - Kali_Hi) << 32) + Mess_Lo - Kali_Lo
I$ = "Anzahl der ausgeführten CPU-Instruktionen : " + StrU(Instructions, #PB_Quad) + #LFCR$
UMode = #Branch_Instructions_Retired | #Bit16 | #Bit22
Case 3
Jumps = ((Mess_Hi - Kali_Hi) << 32) + Mess_Lo - Kali_Lo ;auch Calls und Returns!
J$ = "Anzahl der ausgeführten Sprünge (incl. Calls und Returns) : " + StrU(Jumps, #PB_Quad) + #LFCR$
EndSelect
Next
MessageRequester("Auswertung Testcode", C$ + I$ + J$)
EndIf
WR0_WrmsrTx(#IA32_PERF_GLOBAL_CTRL, Lo_Old, Hi_Old, TMask) ;Counter0 wieder auf Ursprungs-Wert setzen
WR0_DeinitializeOls()
SetThreadPriority_(hThread, Old_Priority) ;Ordung muss sein!
EndIf
EndIf
Helle
Edit 5.10.2009: MissesJumps entfernt, da mein Test-I7 dies so nicht mochte. Rest Kosmetik.
Edit 22.05.2011: Core-Zuordnung korrigiert.