Major improvements in algoritms of CPU load balancing. This should
make polling cycles more regular and changes in CPU load more smooth.


git-svn-id: svn://svn.code.sf.net/p/tigervnc/code/trunk@497 3789f03b-4d11-0410-bbf8-ca57d06f2519
diff --git a/x0vncserver/PollingScheduler.cxx b/x0vncserver/PollingScheduler.cxx
index af597df..c9d8d60 100644
--- a/x0vncserver/PollingScheduler.cxx
+++ b/x0vncserver/PollingScheduler.cxx
@@ -23,6 +23,10 @@
 #include <string.h>
 #include <stdlib.h>
 
+#ifdef DEBUG
+#include <stdio.h>
+#endif
+
 #include <x0vncserver/PollingScheduler.h>
 
 PollingScheduler::PollingScheduler(int interval, int maxload)
@@ -69,6 +73,7 @@
     memset(m_slept, 0, sizeof(m_slept));
     m_sleptSum = 0;
     m_idx = 0;
+    m_count = 0;
 
   } else {
 
@@ -95,26 +100,63 @@
     m_errorAbsSum = m_errorAbsSum - abs(oldest) + abs(newError);
 
     //
-    // Here is the most important part.
+    // Below 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;
+    // Estimation based on keeping up constant interval.
+    m_ratedDuration = m_interval - m_errorSum / 2;
 
+    // Estimations based on keeping up desired CPU load.
+    int optimalLoadDuration1 = 0;
+    int optimalLoadDuration8 = 0;
+    int optimalLoadDuration = 0;
+
+    if (m_count > 4) {
+      // Estimation 1 (use previous pass statistics).
+      optimalLoadDuration1 =
+        ((duration - m_sleptThisPass) * 100 + m_maxload/2) / m_maxload;
+
+      if (m_count > 16) {
+        // Estimation 2 (use history of 8 previous passes).
+        optimalLoadDuration8 =
+          ((m_durationSum - m_sleptSum) * 900 + m_maxload*4) / (m_maxload*8)
+          - m_durationSum;
+        // Mix the above two giving more priority to the first.
+        optimalLoadDuration =
+          (2 * optimalLoadDuration1 + optimalLoadDuration8) / 3;
+      } else {
+        optimalLoadDuration = optimalLoadDuration1;
+      }
+    }
+
+#ifdef DEBUG
+    fprintf(stderr, "<est %3d,%3d,%d>\t",
+            m_ratedDuration, optimalLoadDuration1, optimalLoadDuration8);
+#endif
+
+    // Choose final estimation.
     if (m_ratedDuration < optimalLoadDuration) {
       m_ratedDuration = optimalLoadDuration;
     }
-
     if (m_ratedDuration < 0) {
       m_ratedDuration = 0;
+    } else if (m_ratedDuration > 500 && m_interval <= 100) {
+      m_ratedDuration = 500;
+    } else if (m_ratedDuration > 1000) {
+      m_ratedDuration = 1000;
     }
 
-    // Update ring buffer indexer (8 elements in the arrays).
+#ifdef DEBUG
+    fprintf(stderr, "<final est %3d>\t", m_ratedDuration);
+#endif
+
+    // Update ring buffer indexer (8 elements per each arrays).
     m_idx = (m_idx + 1) & 7;
 
+    // Update pass counter.
+    m_count++;
+
   }
 
   m_passStarted = timeNow;