[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.