]> source.dussan.org Git - tigervnc.git/commitdiff
Improved bandwidth monitoring
authorPierre Ossman <ossman@cendio.se>
Thu, 21 May 2020 09:31:40 +0000 (11:31 +0200)
committerPierre Ossman <ossman@cendio.se>
Thu, 21 May 2020 09:43:51 +0000 (11:43 +0200)
Now measures over an entire update, which should hopefully give us more
stable values. They are still small values for fast networks though so
increase precision in the values we keep.

common/rdr/FdInStream.cxx
common/rdr/FdInStream.h
vncviewer/CConn.cxx
vncviewer/CConn.h

index d27533884bad7965089e7f93fd0a1022d17297ca..27de92bbf0f5c5d8b2b7c3183de0d5e587b44144 100644 (file)
@@ -51,14 +51,12 @@ enum { DEFAULT_BUF_SIZE = 8192 };
 FdInStream::FdInStream(int fd_, int timeoutms_,
                        bool closeWhenDone_)
   : fd(fd_), closeWhenDone(closeWhenDone_),
-    timeoutms(timeoutms_), blockCallback(0),
-    timing(false), timeWaitedIn100us(5), timedKbits(0)
+    timeoutms(timeoutms_), blockCallback(0)
 {
 }
 
 FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_)
-  : fd(fd_), timeoutms(0), blockCallback(blockCallback_),
-    timing(false), timeWaitedIn100us(5), timedKbits(0)
+  : fd(fd_), timeoutms(0), blockCallback(blockCallback_)
 {
 }
 
@@ -104,10 +102,6 @@ bool FdInStream::fillBuffer(size_t maxSize, bool wait)
 
 size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
 {
-  struct timeval before, after;
-  if (timing)
-    gettimeofday(&before, 0);
-
   int n;
   while (true) {
     do {
@@ -144,48 +138,5 @@ size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
   if (n < 0) throw SystemException("read",errno);
   if (n == 0) throw EndOfStream();
 
-  if (timing) {
-    gettimeofday(&after, 0);
-    int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
-                         (after.tv_usec - before.tv_usec) / 100);
-    int newKbits = n * 8 / 1000;
-
-    // limit rate to between 10kbit/s and 40Mbit/s
-
-    if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
-    if (newTimeWaited < newKbits/4)    newTimeWaited = newKbits/4;
-
-    timeWaitedIn100us += newTimeWaited;
-    timedKbits += newKbits;
-  }
-
   return n;
 }
-
-void FdInStream::startTiming()
-{
-  timing = true;
-
-  // Carry over up to 1s worth of previous rate for smoothing.
-
-  if (timeWaitedIn100us > 10000) {
-    timedKbits = timedKbits * 10000 / timeWaitedIn100us;
-    timeWaitedIn100us = 10000;
-  }
-}
-
-void FdInStream::stopTiming()
-{
-  timing = false; 
-  if (timeWaitedIn100us < timedKbits/2)
-    timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
-}
-
-unsigned int FdInStream::kbitsPerSecond()
-{
-  // The following calculation will overflow 32-bit arithmetic if we have
-  // received more than about 50Mbytes (400Mbits) since we started timing, so
-  // it should be OK for a single RFB update.
-
-  return timedKbits * 10000 / timeWaitedIn100us;
-}
index 82280f9f1bc1834addfde1540145051ec29840e7..0203389b6a57c4495cf8acb748449cc866641ed9 100644 (file)
@@ -45,11 +45,6 @@ namespace rdr {
     void setBlockCallback(FdInStreamBlockCallback* blockCallback);
     int getFd() { return fd; }
 
-    void startTiming();
-    void stopTiming();
-    unsigned int kbitsPerSecond();
-    unsigned int timeWaited() { return timeWaitedIn100us; }
-
   private:
     virtual bool fillBuffer(size_t maxSize, bool wait);
 
@@ -60,10 +55,6 @@ namespace rdr {
     int timeoutms;
     FdInStreamBlockCallback* blockCallback;
 
-    bool timing;
-    unsigned int timeWaitedIn100us;
-    unsigned int timedKbits;
-
     size_t offset;
     U8* start;
   };
index 68f4144590523b7ab5cc65fe0d33038f20cc900f..e7362c8ee718d42e111cd2364fa1267ebb2d5707 100644 (file)
@@ -75,7 +75,7 @@ static const PixelFormat mediumColourPF(8, 8, false, true,
 CConn::CConn(const char* vncServerName, network::Socket* socket=NULL)
   : serverHost(0), serverPort(0), desktop(NULL),
     updateCount(0), pixelCount(0),
-    lastServerEncoding((unsigned int)-1)
+    lastServerEncoding((unsigned int)-1), bpsEstimate(20000000)
 {
   setShared(::shared);
   sock = socket;
@@ -196,7 +196,7 @@ const char *CConn::connectionInfo()
   strcat(infoText, "\n");
 
   snprintf(scratch, sizeof(scratch),
-           _("Line speed estimate: %d kbit/s"), sock->inStream().kbitsPerSecond());
+           _("Line speed estimate: %d kbit/s"), (int)(bpsEstimate/1000));
   strcat(infoText, scratch);
   strcat(infoText, "\n");
 
@@ -345,6 +345,10 @@ void CConn::framebufferUpdateStart()
 {
   CConnection::framebufferUpdateStart();
 
+  // For bandwidth estimate
+  gettimeofday(&updateStartTime, NULL);
+  updateStartPos = sock->inStream().pos();
+
   // Update the screen prematurely for very slow updates
   Fl::add_timeout(1.0, handleUpdateTimeout, this);
 }
@@ -355,10 +359,29 @@ void CConn::framebufferUpdateStart()
 // appropriately, and then request another incremental update.
 void CConn::framebufferUpdateEnd()
 {
+  unsigned long long elapsed, bps;
+  struct timeval now;
+
   CConnection::framebufferUpdateEnd();
 
   updateCount++;
 
+  // Calculate bandwidth everything managed to maintain during this update
+  gettimeofday(&now, NULL);
+  elapsed = (now.tv_sec - updateStartTime.tv_sec) * 1000000;
+  elapsed += now.tv_usec - updateStartTime.tv_usec;
+  if (elapsed == 0)
+    elapsed = 1;
+  bps = (unsigned long long)(sock->inStream().pos() -
+                             updateStartPos) * 8 *
+                            1000000 / elapsed;
+  // Allow this update to influence things more the longer it took, to a
+  // maximum of 20% of the new value.
+  if (elapsed > 2000000)
+    elapsed = 2000000;
+  bpsEstimate = ((bpsEstimate * (10000000 - elapsed)) +
+                 (bps * elapsed)) / 10000000;
+
   Fl::remove_timeout(handleUpdateTimeout, this);
   desktop->updateWindow();
 
@@ -381,15 +404,11 @@ void CConn::bell()
 
 void CConn::dataRect(const Rect& r, int encoding)
 {
-  sock->inStream().startTiming();
-
   if (encoding != encodingCopyRect)
     lastServerEncoding = encoding;
 
   CConnection::dataRect(r, encoding);
 
-  sock->inStream().stopTiming();
-
   pixelCount += r.area();
 }
 
@@ -459,28 +478,22 @@ void CConn::resizeFramebuffer()
 //
 void CConn::autoSelectFormatAndEncoding()
 {
-  int kbitsPerSecond = sock->inStream().kbitsPerSecond();
-  unsigned int timeWaited = sock->inStream().timeWaited();
   bool newFullColour = fullColour;
   int newQualityLevel = ::qualityLevel;
 
   // Always use Tight
   setPreferredEncoding(encodingTight);
 
-  // Check that we have a decent bandwidth measurement
-  if ((kbitsPerSecond == 0) || (timeWaited < 10000))
-    return;
-
   // Select appropriate quality level
   if (!noJpeg) {
-    if (kbitsPerSecond > 16000)
+    if (bpsEstimate > 16000000)
       newQualityLevel = 8;
     else
       newQualityLevel = 6;
 
     if (newQualityLevel != ::qualityLevel) {
       vlog.info(_("Throughput %d kbit/s - changing to quality %d"),
-                kbitsPerSecond, newQualityLevel);
+                (int)(bpsEstimate/1000), newQualityLevel);
       ::qualityLevel.setParam(newQualityLevel);
       setQualityLevel(newQualityLevel);
     }
@@ -498,14 +511,14 @@ void CConn::autoSelectFormatAndEncoding()
   }
   
   // Select best color level
-  newFullColour = (kbitsPerSecond > 256);
+  newFullColour = (bpsEstimate > 256000);
   if (newFullColour != fullColour) {
     if (newFullColour)
       vlog.info(_("Throughput %d kbit/s - full color is now enabled"),
-                kbitsPerSecond);
+                (int)(bpsEstimate/1000));
     else
       vlog.info(_("Throughput %d kbit/s - full color is now disabled"),
-                kbitsPerSecond);
+                (int)(bpsEstimate/1000));
     fullColour.setParam(newFullColour);
     updatePixelFormat();
   } 
index 4d935c96c378d4be596a72999cefbc341463015f..25dff875abd62d8a0f86544eb29cb3a524d9f7c3 100644 (file)
@@ -101,6 +101,10 @@ private:
   rfb::PixelFormat fullColourPF;
 
   int lastServerEncoding;
+
+  struct timeval updateStartTime;
+  size_t updateStartPos;
+  unsigned long long bpsEstimate;
 };
 
 #endif