question about --bwlimit=

Wayne Davison wayned at samba.org
Mon May 24 23:20:14 GMT 2004


On Mon, May 24, 2004 at 01:54:42PM -0700, Wayne Davison wrote:
> I'm looking into some of the old bwlimit patches to see about
> improving this.

Here's a potential patch to make --bwlimit better.  This started with
Roger's idea on accumulating delay until we have enough to make a sleep
call without significant rounding error, but I modified it to keep count
in bytes written so that we should avoid the problem discovered when
bwlimit is 4001KBPS or larger.  The patch subtracts out elapsed time
since the last call to sleep_for_bwlimit() (but only in a limited way)
and also makes note of any rounding after the sleep() call when it
resets the counter.  I also changed the use of 1000 for "K" to 1024 so
that it would more closely match the value reported by the progress
output.  Finally, I applied a modified version of the patch that Paul
just reminded us that Debian is using, though I decided to limit the
write size to "bwlimit * 512" rather than "bwlimit * 100" (at least for
now, but feel free to argue that a different value is better).

Comments?  Is this overkill?  Does it have flaws?  In my limited testing
this made the bwlimit more accurate.

..wayne..
-------------- next part --------------
--- io.c	15 May 2004 19:31:10 -0000	1.121
+++ io.c	24 May 2004 22:58:14 -0000
@@ -739,10 +739,22 @@ unsigned char read_byte(int f)
  * use a bit less bandwidth than specified, because it doesn't make up
  * for slow periods.  But arguably this is a feature.  In addition, we
  * ought to take the time used to write the data into account.
+ *
+ * During some phases of big transfers (file FOO is uptodate) this is
+ * called with a small bytes_written every time.  As the kernel has to
+ * round small waits up to guarantee that we actually wait at least the
+ * requested number of microseconds, this can become grossly inaccurate.
+ * We therefore keep track of the bytes we've written over time and only
+ * sleep when the accumulated delay is at least 1 tenth of a second.
  **/
 static void sleep_for_bwlimit(int bytes_written)
 {
-	struct timeval tv;
+	static struct timeval prior_tv;
+	static long total_written = 0; 
+	struct timeval tv, start_tv;
+	long elapsed_usec, sleep_usec;
+
+#define ONE_uSEC	1000000L
 
 	if (!bwlimit)
 		return;
@@ -750,11 +762,31 @@ static void sleep_for_bwlimit(int bytes_
 	assert(bytes_written > 0);
 	assert(bwlimit > 0);
 
-	tv.tv_usec = bytes_written * 1000 / bwlimit;
-	tv.tv_sec  = tv.tv_usec / 1000000;
-	tv.tv_usec = tv.tv_usec % 1000000;
+	total_written += bytes_written; 
+
+	gettimeofday(&start_tv, NULL);
+	if (prior_tv.tv_sec) {
+		elapsed_usec = (start_tv.tv_sec - prior_tv.tv_sec) * ONE_uSEC
+			     + (start_tv.tv_usec - prior_tv.tv_usec);
+		total_written -= elapsed_usec * bwlimit / (ONE_uSEC/1024);
+		if (total_written < 0)
+			total_written = 0;
+	}
 
+	sleep_usec = total_written * (ONE_uSEC/1024) / bwlimit;
+	if (sleep_usec < ONE_uSEC / 10) {
+		prior_tv = start_tv;
+		return;
+	}
+
+	tv.tv_sec  = sleep_usec / ONE_uSEC;
+	tv.tv_usec = sleep_usec % ONE_uSEC;
 	select(0, NULL, NULL, NULL, &tv);
+
+	gettimeofday(&prior_tv, NULL);
+	elapsed_usec = (prior_tv.tv_sec - start_tv.tv_sec) * ONE_uSEC
+		     + (prior_tv.tv_usec - start_tv.tv_usec);
+	total_written = (sleep_usec - elapsed_usec) * bwlimit / (ONE_uSEC/1024);
 }
 
 
@@ -812,6 +844,8 @@ static void writefd_unbuffered(int fd,ch
 		if (FD_ISSET(fd, &w_fds)) {
 			int ret;
 			size_t n = len-total;
+			if (bwlimit && n > (size_t)bwlimit * 512)
+				n = (size_t)bwlimit * 512;
 			ret = write(fd,buf+total,n);
 
 			if (ret < 0) {


More information about the rsync mailing list