From 7de44e0a428f88f88483076a19c173f004de9e46 Mon Sep 17 00:00:00 2001 From: Ben Greear Date: Fri, 26 Mar 2010 23:33:02 +0100 Subject: Make rate-limitation logic smoother This gives a smoother rate limitation performance by using sub-second pauses and also taking the buffer sizes into account. --- lib/transfer.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 5 deletions(-) (limited to 'lib/transfer.c') diff --git a/lib/transfer.c b/lib/transfer.c index ad9499c99..2d504050c 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1175,6 +1175,60 @@ int Curl_single_getsock(const struct connectdata *conn, return bitmap; } +/* + * Determine optimum sleep time based on configured rate, current rate, + * and packet size. + * Returns value in mili-seconds. + * + * The basic idea is to adjust the desired rate up/down in this method + * based on whether we are running too slow or too fast. Then, calculate + * how many miliseconds to wait for the next packet to achieve this new + * rate. + */ +long Curl_sleep_time(curl_off_t rate_bps, curl_off_t cur_rate_bps, + int pkt_size) +{ + curl_off_t min_sleep = 0; + curl_off_t rv = 0; + + if (rate_bps == 0) + return 0; + + /* If running faster than about .1% of the desired speed, slow + * us down a bit. Use shift instead of division as the 0.1% + * cutoff is arbitrary anyway. + */ + if (cur_rate_bps > (rate_bps + (rate_bps >> 10))) { + /* running too fast, decrease target rate by 1/64th of rate */ + rate_bps -= rate_bps >> 6; + min_sleep = 1; + } + else if (cur_rate_bps < (rate_bps - (rate_bps >> 10))) { + /* running too slow, increase target rate by 1/64th of rate */ + rate_bps += rate_bps >> 6; + } + + /* Determine number of miliseconds to wait until we do + * the next packet at the adjusted rate. We should wait + * longer when using larger packets, for instance. + */ + rv = ((curl_off_t)((pkt_size * 8) * 1000) / rate_bps); + + /* Catch rounding errors and always slow down at least 1ms if + * we are running too fast. + */ + if (rv < min_sleep) + rv = min_sleep; + + /* Bound value to fit in 'long' on 32-bit platform. That's + * plenty long enough anyway! + */ + if(rv > 0x7fffffff) + rv = 0x7fffffff; + + return (long)rv; +} + /* * Transfer() @@ -1197,6 +1251,8 @@ Transfer(struct connectdata *conn) bool done=FALSE; bool first=TRUE; int timeout_ms; + int buffersize; + int totmp; if((conn->sockfd == CURL_SOCKET_BAD) && (conn->writesockfd == CURL_SOCKET_BAD)) @@ -1211,6 +1267,7 @@ Transfer(struct connectdata *conn) curl_socket_t fd_read = conn->sockfd; curl_socket_t fd_write = conn->writesockfd; int keepon = k->keepon; + timeout_ms = 1000; if(conn->waitfor) { /* if waitfor is set, get the RECV and SEND bits from that but keep the @@ -1228,6 +1285,16 @@ Transfer(struct connectdata *conn) k->keepon &= ~KEEP_SEND_HOLD; } else { + if (data->set.upload && data->set.max_send_speed && + (data->progress.ulspeed > data->set.max_send_speed) ) { + /* calculate upload rate-limitation timeout. */ + buffersize = (int)(data->set.buffer_size ? + data->set.buffer_size : BUFSIZE); + totmp = (int)Curl_sleep_time(data->set.max_send_speed, + data->progress.ulspeed, buffersize); + if (totmp < timeout_ms) + timeout_ms = totmp; + } fd_write = CURL_SOCKET_BAD; if(keepon & KEEP_SEND) k->keepon |= KEEP_SEND_HOLD; /* hold it */ @@ -1239,6 +1306,16 @@ Transfer(struct connectdata *conn) k->keepon &= ~KEEP_RECV_HOLD; } else { + if ((!data->set.upload) && data->set.max_recv_speed && + (data->progress.dlspeed > data->set.max_recv_speed)) { + /* Calculate download rate-limitation timeout. */ + buffersize = (int)(data->set.buffer_size ? + data->set.buffer_size : BUFSIZE); + totmp = (int)Curl_sleep_time(data->set.max_recv_speed, + data->progress.dlspeed, buffersize); + if (totmp < timeout_ms) + timeout_ms = totmp; + } fd_read = CURL_SOCKET_BAD; if(keepon & KEEP_RECV) k->keepon |= KEEP_RECV_HOLD; /* hold it */ @@ -1267,14 +1344,15 @@ Transfer(struct connectdata *conn) timeout_ms = 0; else { if(data->set.timeout) { - timeout_ms = data->set.timeout - Curl_tvdiff(k->now, k->start); - if(timeout_ms > 1000) - timeout_ms = 1000; - else if(timeout_ms < 0) + totmp = (int)(data->set.timeout - Curl_tvdiff(k->now, k->start)); + if(totmp < 0) return CURLE_OPERATION_TIMEDOUT; } else - timeout_ms = 1000; + totmp = 1000; + + if (totmp < timeout_ms) + timeout_ms = totmp; } switch (Curl_socket_ready(fd_read, fd_write, timeout_ms)) { -- cgit v1.2.3