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



  • moetan schrieb:

    du hast seit über einer Woche keinen Neustart gemacht und es läuft immer noch stabil und einigermaßen schnell? Das wäre echt ein Wunder!

    Keine Auslagerungsdatei, keine Viren, kein Virenscanner, ich arbeite nie als Admin, es ist erst ca ein Jahr alt, die Terabyteplatte ist nur zu 16% voll.
    Im Moment ist die Uptime kleiner als eine Woche, weil ich öfters mal zum Linux auf der anderen Platte umgebootet habe. Aber ich habe nie gebootet, weil er langsam geworden wäre und abstürzen tut er auch nicht. In der Küche habe ich einen XP-Rechner stehen, der macht nur einen auf Fileserver. Mal nachgucken...
    Ähm, leichter gesagt als getan, wie frage ich denn die uptime ab?
    edit: Habs gefunden.

    C:\irgendwo>net statistics server
    Serverstatistik für \\IRGENDWER
    
    Statistik seit 6/15/2010 6:37 PM
    ...
    


  • Ich dachte es geht gerade drum unter 15ms zu kommen???
    Windows teilt immer die verfügbare Zeit unter allen Threads auf. Es braucht kein Sleep oder so!

    Das Sleep ist nur da um Windows mitzuteilen das man einige Zeit nichts zu tun hat. Das ist normalerweise sinnvoll weil andere Threads diese Zeit nutzen können. Es gibt aber eine minimale Zeit(bei XP wohl so um die 15ms). Wenn man also wie in diesem Fall gar keine anderen Threads hat die wirklich die Zeit brauchen macht es ja gar keinen Sinn.



  • Wir sprechen hier von Extremsituationen, da hat jeder seine eigene Meinung. Jedenfalls ist Windows für sowas ganz sicher nicht gedacht. Ich würde dann auch eher zum Mikrocontroller raten. Da kann kein anderer Thread dazwischen kommen. 😃



  • @volkard

    Ich glaube moetan meinte industrielle Anwendungen. Da hat Windows permanent ordentlich was zu tun und nach 1-2 Wochen wird es deutlich langsamer. Deshalb startet man normalerweise immer am WE neu und hat diese Montagsprobleme nicht.



  • @hustbaer: Danke. Hab gerade mal deinen code ausprobiert, auch auf nem anderen Rechner und da dann mit VC 2010 express, aber unter 15ms komme ich trotzdem nicht, obwohl ( timeBeginPeriod(min) == TIMERR_NOERROR ) bei mir auch true ist. Der Bill ist mal wieder so fies zu mir. 😉
    Ich werd dann nochmal berichten, wie schlimm die Abweichung mit dem Performance-Counter ist und was meine weiteren Test von Geekys Sleep-Variante ergeben.

    @Microcontrollervorschlag: Ja, es mag sein, dass es elegantere Lösungen für diese Sache gibt, aber die mir vorgegebene Hardwarekonfiguration ist nun schon vielfach vorhanden und lässt sich aus Kostengründen deshalb nicht mehr ändern.

    @loybyte: ><(({°>
    😉



  • ich stand vor dem gleichen Problem und habe aufgegeben. Man kann mit einem Multicore Prozessor nicht mehr unter die 15ms kommen. Jedenfalls nicht einigermaßen sicher. Oder hat sich noch eine brauchbare Lösung gefunden?



  • was hat das mit der CPU zu tun? Das ist doch eine Sache die Windows regelt?



  • Mir wurde mehrfach berichtet das man mit einer "normalen" CPU deutlich unter die 15ms kommt. Warum das so ist und ob es wirklich so ist kann ich nicht erklären. Es ist mir sowiso ein Rätsel wie Windows es bei jedem Thread jeder Anwendung schaffen kann mit dieser Präzision immer genau die 15ms einzuhalten.



  • 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