[gelöst] Zeitauflösung von Sleep und co. erhöhen?



  • Das dürfte dann wohl eine Sache der Hardware sein. Aber wenn das wirklich so ist wird Windows nur feststellen das die entsprechende CPU vorhanden ist und dann so reagieren.

    Deine "Idee" wäre das jeder Thread jeder Anwendung genau 15ms(oder weniger)wartet. Das hat man aber idr nicht.



  • Hier ein paar Hilfsfunktionen die ich mir mal geschrieben hatte.

    void sleep(double usleept)
    {
    	if(usleept >= 0)
    	{ // Nur wenn zu schlafen ist
    		struct timeval tv;
    		tv.tv_sec = floor(usleept);
    		tv.tv_usec = fround((usleept - floor(usleept)) * 1000000); // s -> us // 500 ms = 500 000 us;
    
    		fd_set dummy;
    		SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
    		FD_ZERO(&dummy);
    		FD_SET(s, &dummy);
    
    		select(0, NULL, NULL, &dummy, &tv);
    		closesocket(s);
    	}
    }
    
    bool setcyclefrequency( double frequency, bool noadjustment /*= false*/ )
    {
    	// Es muss ein Core ausgesucht werden als Zeitgeber
    	HANDLE hProcess = GetCurrentProcess();
    	m_dwAffinityMask = 0;
    	DWORD dwSystemAffinityMask = 0;
    	GetProcessAffinityMask(hProcess, &m_dwAffinityMask, &dwSystemAffinityMask);
    
    	unsigned int iProzessor = 0;
    	for(iProzessor = 0; iProzessor < sizeof(m_dwAffinityMask)*8; iProzessor)
    	{
    		DWORD dwAffinityMask = m_dwAffinityMask & (((DWORD)1) << iProzessor);
    		if(dwAffinityMask)
    		{
    			m_dwAffinityMask = dwAffinityMask;
    			break; // Ersten Prozessor wählen
    		}
    	}
    
    	if(!m_dwAffinityMask)
    		return false;
    
    	// Immer auf gleichem Prozessor ausführen
    	m_hThread = GetCurrentThread();
    	DWORD oldAffinityMask = SetThreadAffinityMask(m_hThread, m_dwAffinityMask);
    	if(!oldAffinityMask)
    		return false;
    
    	if(!QueryPerformanceFrequency((LARGE_INTEGER*)&m_clockFrequency)) // zeit in clocks pro sekunde
    		return false;
    
    	if(frequency > 1000000) // Max 1 MHz / 1 us (theoretisch) // real max 10 KHz
    		frequency = 1000000;
    
    	m_cycleTime			= (1.0/frequency); // Zeit in Clocks die ein Zyklus dauert
    	m_bAdjustment		= !noadjustment;
    
    	SetThreadAffinityMask(m_hThread, oldAffinityMask);
    
    	return resetcycle();
    }
    
    bool waitfornextcycle()
    {
    	m_cycles++;
    
    	// Immer auf gleichem Prozessor ausführen
    	DWORD oldAffinityMask = SetThreadAffinityMask(m_hThread, m_dwAffinityMask);
    	if(!oldAffinityMask)
    		return false;
    
    	if(!m_bInit) {
    		m_dwstartTime = m_dwcurrentTime = GetTickCount();
    		m_bInit = true;
    	} else
    		m_dwcurrentTime = GetTickCount();
    
    	if(m_dwcurrentTime < m_dwlastTime) // Overflow in Timer ~50 Tage
    	{
    		m_dwstartTime	= m_dwcurrentTime;
    		m_dwlastTime	= ((~(DWORD)0) - m_dwlastTime) + m_dwcurrentTime;
    		m_cycles		= 1;
    	}
    
    	if(0 == m_cycles) // Overflow Zykluszähler (Taktabhängig)
    	{
    		m_dwstartTime	= m_dwcurrentTime;
    		m_cycles		= 1;
    	}
    
    	// Overflow des PerfomanceCounters sind bei 3 Ghz circa 180 Jahre also nicht relevant -> Rückfall auf 10 ms Auflösung in diesem Fall da hohe differenz
    
    	if(!QueryPerformanceCounter((LARGE_INTEGER*)&m_currentTime))
    		return false;
    
    	double QPCdiff		 = m_currentTime - m_lastTime;
    	double GTCdiff		 = m_dwcurrentTime - m_dwlastTime;
    	double GTCdiff_total = m_dwcurrentTime - m_dwstartTime;
    
    	QPCdiff			/= m_clockFrequency; // clocks	-> s
    	GTCdiff			/= 1000;			 // ms		-> s
    	GTCdiff_total	/= 1000;			 // ms		-> s
    
    	if(fabs(QPCdiff - GTCdiff) > 0.1) // mehr als 100ms Differenz -> QPC springt oder overflowt, GTC timer Korrektur (bei langen Zyklusdauern) // Oder Overflow im Performance Counter
    		QPCdiff = GTCdiff; // Fallback zu ms Auflösung in diesem Takt
    
    	m_nLostCycles = fround((GTCdiff_total / m_cycleTime) - m_cycles);
    
    	// Genauerer Sleeps über BSD Socket select Funktion
    	if(m_nLostCycles <= 0 || !m_bAdjustment)
    	{ // Nur wenn momentan keine Takte verlorengegangen sind oder Takt Korrektur abgeschaltet ist
    
    		double usleept = m_cycleTime - QPCdiff; // Berechne wie lange wir schlafen duerfen anhand der vergangen Zeit leit letzem Aufruf
    
    		sleep(usleept);
    	}
    
    	// Frequenz hier nochmal aktualisieren angeblich ändert sie sich trotz MSDN trotzdem manchmal
    	if(!QueryPerformanceFrequency((LARGE_INTEGER*)&m_clockFrequency)) // zeit in clocks pro sekunde
    		return false;
    
    	m_dwlastTime = GetTickCount();
    	if(!QueryPerformanceCounter((LARGE_INTEGER*)&m_lastTime))
    		return false;
    
    	SetThreadAffinityMask(m_hThread, oldAffinityMask);
    
    	return true;
    }
    
    bool resetcycle()
    {
    	m_lastTime		= 0;
    	m_currentTime	= 0;
    
    	m_dwstartTime	= 0;
    	m_dwlastTime	= 0;
    	m_dwcurrentTime	= 0;
    	m_bInit			= false;
    	m_cycles		= 0;
    	m_nLostCycles	= 0;
    
    	// Immer auf gleichem Prozessor ausführen
    	DWORD oldAffinityMask = SetThreadAffinityMask(m_hThread, m_dwAffinityMask);
    	if(!oldAffinityMask)
    		return false;
    
    	m_dwlastTime = GetTickCount();
    	if(!QueryPerformanceCounter((LARGE_INTEGER*)&m_lastTime))
    		return false;
    
    	SetThreadAffinityMask(m_hThread, oldAffinityMask);
    
    	return true;
    }
    
    bool isfrequencyok()
    {
    	// Wenn wir über 10 Sekunden nachhängen ist der Takt nicht realisierbar oder es wird debuggt etc.
    	bool bRet = ((double)m_nLostCycles * m_cycleTime) <= 10.0;
    
    	if(!bRet)
    		return false;
    
    	return bRet;
    }
    

    Frequenz einmal initialisieren und dann einfach immer waitfornextcycle aufrufen sorgt dafür das die Frequenz eingehalten wird. (bei mir bis zu 10 khz)



  • ganz schön heftig...aber 10Khz wären schon 0,1ms...

    Muß ich mir mal in Ruhe anschauen.



  • So, nochmal vielen Dank für die Hilfe und ich wollte mitteilen, wie es für mich ausgegangen ist.

    Das normale Sleep ist für meine Zwecke schon genau genug (1ms).
    Für die genaue Uhrzeit verwende ich das hier:
    http://msdn.microsoft.com/en-us/magazine/cc163996.aspx
    Dazu die kleine Änderung, dass ich immer QueryPerformanceCounter benutze, weil TSC trotz vorherigem CPUID auf manchen alten Athlons Käse ausgibt.


Anmelden zum Antworten