Funktion die Millisekunden genau ist



  • Zählt dein Zähler die Anzahl der Takte? Wie funktioniert des bei meinem AMD64 3400+ der mit 2,2 GHz arbeitet? Wie rechnest du das in eine Zeiteinheit um?



  • spl@t schrieb:

    mov DWORD [ebx], edx  // die hier!
            mov DWORD [ecx], eax  //
    

    Was muss ich noch ändern?

    Entweder du lässt das DWORD weg, denn aufgrund von edx bzw eax kann der Compiler implizit die Datenbreite herleiten. Oder du schreibst:

    mov dword ptr [ebx], edx  // die hier!
            mov dword ptr [ecx], eax  //
    

    Für Zeitmessungen würde ich dir aber rdtsc nicht empfehlen. Meines Wissens gibt es zwar Möglichkeiten die Frequenz mit der rdtsc arbeitet zu ermitteln und damit Zeitwerte zu errechnen, wie portabel das aber von CPU Typ zu CPU Typ ist, kann ich dir nicht sagen.
    Nimm einfach die Performance Counter, dann brauchst du dich nicht mit Assemblercode rumzuschlagen und hast zumindest die Gewissheit, dass das auf aktuellen Windows Systemen funktioniert. Pack das ganze in 'ne schöne Klasse, dann wird die Benutzung einfacher und du bleibst portabel für andere Systeme.



  • flammenvogel schrieb:

    @kingruedi: Soweit ich weiß ist der PerformanceCounter ein extra Hardware Baustein und nicht Software abhängig. Die Genaugkeit ist desshalb wichtig, weil mein Spiel noch in der Anfangsphase steckt -> nicht viel Code hat -> die Ausführung der Spielschleife auf meinem Rechner weniger als eine Millisekunde dauert. Daher können die meisten Funktionen die ich kenne keine Differenz messen.

    Das Problem ist der Scheduler, der ja zwischen durch auch anderen Prozessen Zeit gewährt.



  • Das ist allerdings nicht so ausschlaggebend, wenn die restlichen Threads ihren Timeslice abgeben. Trotzdem wirst du dieses Problem bei Multitasking BSen immer haben, selbst mit Hardware Multithreading. Da helfen dann nur Critical Sections, nur sind diese nicht für zeitaufwändige Sachen gedacht.



  • Thx, compilieren tut ers jetzt, aber funktionieren tuts nicht. Ich lasse dann wohl wiklich lieber die Finger davon 😉



  • @spl@t:
    Klar, dass es bei dir Inline nicht so funktionieren kann. Es ist auch eine Externe Loesung gewesen, die sich um Ruecksprungadressen & Co kuemmert. Compiliert wurde sie mit NASM unter Linux & Windows.

    Hier VC Inline:

    Check ob RDTSC:

    bool canCPUID = true;
    			int canRDTSC = true;
    
    			__asm {
    				pushfd					; Pushing EFlags
    				pop eax					; Popping EFlags to ebx
    				mov ebx, eax			; Copy eax to ebx
    				xor eax, 00200000h		; Change bit 21
    				push eax				; Push eax
    				popfd					; Get EFlags
    				pushfd					; Store EFlags
    				pop eax					; Get eax
    				xor eax, ebx			; Check if something changed
    				jnz	done				; If it exists - it´s done :)
    				mov canCPUID, 0			; Delete flag
    			done:
    			}
    
    			if( canCPUID ) {
    				__asm {
    					mov eax, 01h		; Processor Info
    					CPUID				; Get Info´s
    
    					//
    					// Processor info
    					//
    
    					; Can it rdtsc?
    					shr edx, 4
    					and edx, 1b			; check stepping
    					mov canRDTSC, edx	; copy flag
    				}
    			} else {
    				canRDTSC = false;
    			}
    

    Aktuelle Ticks auslesen:

    //! RDTSC
    			static inline void GetTicksRDTSC( LARGE_INTEGER &ticks ) {
    				static LARGE_INTEGER tick; tick.QuadPart = 0;
    #ifdef __GNUC__
    				GetRDTSC( tick.HighPart, tick.LowPart );
    #else
    				__asm {
    					rdtsc
    					mov tick.HighPart, edx
    					mov tick.LowPart, eax
    				} 
    #endif
    				ticks = tick;
    			}
    

    Viel Spass damit!

    P.S. die ifdef fuer GNUC nehmen die externe Variante, also mit NASM Compiled.

    Deklarieren tust du den Externen stuff so:

    extern "C" {
    	//! Returns if the CPU suppurts the CPUID mnemonic - External in ASM File
    	extern int volatile CanCPUID(void);
    
    	//! Returns if the CPU suppurts the RDTSC ( Realtime CPU Tick counteer ) mnemonic - External in ASM File
    	extern int volatile CanRDTSC(void);
    
    	//! Returns if the current CPUtick - External in ASM File
    	extern void volatile GetRDTSC( long &high, unsigned long &low );
    }
    

    Brauchst die .o vom ASM nur beim Linken mit angeben, damit er sie verwurstet!

    @Optimizer:
    Auf einem AMD 64 musst du meines Wissens nur darauf achten, dass du eben 64 Bit fuer die Ticks hernimmst.

    Wie du rausbekommst, wieviele Ticks / Sek deine CPU macht?
    Suche dir eine moeglichkeit aus:
    - Lese es aus der CPU aus
    - Lese es unter Win aus der Registry, unter Linux gibts die Info's in einem File
    - Warte etwas, und rechne die Zeit hoch. Je laenger du wartest, desto genauer ist die Zeit. Allerdings bei 0,25 Sek hast du vielleicht 5% Toleranz beim warten, also nicht so krass.



  • Naja egal, mir reichen meine 3,5 Millionen Tics pro Sekunde. 😉



  • Danke @ SnorreDev, es funktioniert einwandfrei, und scheint so ungefähr 2 Milliarden Tics pro Sekunde (+-50 % ) zu geben 😮



  • GetTickCount ist auf jeden Fall zu ungenau. Die Genauigkeit liegt bei ca. 15 Millisekunden.

    GetTickCount:

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <math.h>
    #include <stdio.h>
    
    void main(void)
    {
    	unsigned long Old=0;
    	unsigned long New=0;
    
    	while(true)
    	{
    		Old=New;
    		New=GetTickCount();
    		if ((New-Old)!=0) printf("%u",New-Old);
    	}
    }
    

    PerformanceCounter:

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <math.h>
    #include <stdio.h>
    
    class CTimer
    {
    public:
    	CTimer(void);
    	~CTimer(void);
    
    public:
    	void Update(void);
    	void Reset(void);
    
    public:
    	unsigned long GetHours(void);
    	unsigned long GetMinutes(void);
    	unsigned long GetSeconds(void);
    	unsigned long GetMilliSeconds(void);
    
    private:
    	LARGE_INTEGER Frequency;
    	LARGE_INTEGER Start;
    	LARGE_INTEGER Delta;
    };
    
    CTimer::CTimer(void)
    {
    	QueryPerformanceFrequency(&Frequency);
    	QueryPerformanceCounter(&Start);
    	Delta.QuadPart=0;
    }
    
    CTimer::~CTimer(void)
    {
    }
    
    void CTimer::Update(void)
    {
    	LARGE_INTEGER end;
    
    	QueryPerformanceCounter(&end);
    	Delta.QuadPart=end.QuadPart-Start.QuadPart;
    }
    
    void CTimer::Reset(void)
    {
    	Start.QuadPart=Start.QuadPart+Delta.QuadPart;
    	Delta.QuadPart=0;
    }
    
    unsigned long CTimer::GetHours(void)
    {
    	LONGLONG result;
    
    	result=Delta.QuadPart/(Frequency.QuadPart*3600);
    	if (result>0xffffffff) result=0xffffffff;
    
    	return((unsigned long)result);
    }
    
    unsigned long CTimer::GetMinutes(void)
    {
    	LONGLONG result;
    
    	result=Delta.QuadPart/(Frequency.QuadPart*60);
    	if (result>0xffffffff) result=0xffffffff;
    
    	return((unsigned long)result);
    }
    
    unsigned long CTimer::GetSeconds(void)
    {
    	LONGLONG result;
    
    	result=Delta.QuadPart/Frequency.QuadPart;
    	if (result>0xffffffff) result=0xffffffff;
    
    	return((unsigned long)result);
    }
    
    unsigned long CTimer::GetMilliSeconds(void)
    {
    	LONGLONG result;
    
    	result=(Delta.QuadPart*1000)/Frequency.QuadPart;
    	if (result>0xffffffff) result=0xffffffff;
    
    	return((unsigned long)result);
    }
    
    void main(void)
    {
    	unsigned long Old=0;
    	unsigned long New=0;
    
    	CTimer timer;
    
    	while(true)
    	{
    		timer.Update();
    		Old=New;
    		New=timer.GetMilliSeconds();
    		if ((New-Old)!=0) printf("%u",New-Old);
    	}
    }
    

    Wenn man dann was 100 mal pro Sekunde "timen" will, dann reicht GetTickCount nicht aus, da jeder Frame nur 10 ms dauert.



  • spl@t schrieb:

    Danke @ SnorreDev, es funktioniert einwandfrei, und scheint so ungefähr 2 Milliarden Tics pro Sekunde (+-50 % ) zu geben 😮

    Hoffe, es ist hochaufloesend genug fuer deine Ansprueche 😉


Anmelden zum Antworten