Major improvement of the CPU load optimization implementation. The
CPUMonitor class is not used any more, that work is implemented in the
PollingScheduler class instead. New implementation solves the problem
of "random" sudden changes of CPU load that could be seen in previous
versions. Also, measurement method for CPU utilization has been
changed, the old one did not count CPU utilization increased by other
processes.
git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@496 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/x0vncserver/PollingScheduler.cxx b/x0vncserver/PollingScheduler.cxx
index f326351..af597df 100644
--- a/x0vncserver/PollingScheduler.cxx
+++ b/x0vncserver/PollingScheduler.cxx
@@ -20,17 +20,30 @@
// PollingScheduler class implementation.
//
+#include <string.h>
+#include <stdlib.h>
+
#include <x0vncserver/PollingScheduler.h>
-PollingScheduler::PollingScheduler(int interval)
+PollingScheduler::PollingScheduler(int interval, int maxload)
{
- setInterval(interval);
+ setParameters(interval, maxload);
reset();
}
-void PollingScheduler::setInterval(int interval)
+void PollingScheduler::setParameters(int interval, int maxload)
{
m_interval = interval;
+ m_maxload = maxload;
+
+ if (m_interval < 0) {
+ m_interval = 0;
+ }
+ if (m_maxload < 1) {
+ m_maxload = 1;
+ } else if (m_maxload > 100) {
+ m_maxload = 100;
+ }
}
void PollingScheduler::reset()
@@ -40,8 +53,93 @@
void PollingScheduler::newPass()
{
- m_passStarted.update();
- m_initialState = false;
+ TimeMillis timeNow;
+
+ if (m_initialState) {
+
+ // First polling pass: initialize statistics.
+ m_initialState = false;
+ m_ratedDuration = 0;
+ m_sleeping = 0;
+ memset(m_errors, 0, sizeof(m_errors));
+ m_errorSum = 0;
+ m_errorAbsSum = 0;
+ memset(m_durations, 0, sizeof(m_durations));
+ m_durationSum = 0;
+ memset(m_slept, 0, sizeof(m_slept));
+ m_sleptSum = 0;
+ m_idx = 0;
+
+ } else {
+
+ // Stop sleeping if not yet.
+ if (m_sleeping)
+ sleepFinished();
+
+ // Update statistics on sleeping time and total pass duration.
+ int duration = timeNow.diffFrom(m_passStarted);
+
+ int oldest = m_durations[m_idx];
+ m_durations[m_idx] = duration;
+ m_durationSum = m_durationSum - oldest + duration;
+
+ oldest = m_slept[m_idx];
+ m_slept[m_idx] = m_sleptThisPass;
+ m_sleptSum = m_sleptSum - oldest + m_sleptThisPass;
+
+ // Compute and save the difference between actual and planned time.
+ int newError = duration - m_interval;
+ oldest = m_errors[m_idx];
+ m_errors[m_idx] = newError;
+ m_errorSum = m_errorSum - oldest + newError;
+ m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
+
+ //
+ // Here is the most important part.
+ // Compute desired duration of the upcoming polling pass.
+ //
+ m_ratedDuration = m_interval - m_errorSum;
+
+ int optimalLoadDuration =
+ ((m_durationSum - m_sleptSum) * 900 + m_maxload * 4) / (m_maxload * 8)
+ - m_durationSum;
+
+ if (m_ratedDuration < optimalLoadDuration) {
+ m_ratedDuration = optimalLoadDuration;
+ }
+
+ if (m_ratedDuration < 0) {
+ m_ratedDuration = 0;
+ }
+
+ // Update ring buffer indexer (8 elements in the arrays).
+ m_idx = (m_idx + 1) & 7;
+
+ }
+
+ m_passStarted = timeNow;
+ m_sleptThisPass = 0;
+}
+
+void PollingScheduler::sleepStarted()
+{
+ if (m_initialState || m_sleeping)
+ return;
+
+ m_sleepStarted.update();
+
+ m_sleeping = true;
+}
+
+void PollingScheduler::sleepFinished()
+{
+ if (m_initialState || !m_sleeping)
+ return;
+
+ TimeMillis timeNow;
+ m_sleptThisPass += timeNow.diffFrom(m_sleepStarted);
+
+ m_sleeping = false;
}
int PollingScheduler::millisRemaining() const
@@ -52,13 +150,20 @@
TimeMillis timeNow;
int elapsed = timeNow.diffFrom(m_passStarted);
- if (elapsed > m_interval)
+ if (elapsed > m_ratedDuration)
return 0;
- return (m_interval - elapsed);
+ return (m_ratedDuration - elapsed);
}
bool PollingScheduler::goodTimeToPoll() const
{
- return (millisRemaining() == 0);
+ if (m_initialState)
+ return true;
+
+ // Average error (per 8 elements in the ring buffer).
+ int errorAvg = (m_errorAbsSum + 4) / 8;
+
+ // It's ok to poll earlier if new error is no more than half-average.
+ return (millisRemaining() <= errorAvg / 2);
}