def __init__(self, external_add_task, config, set_option, get_remote_endpoints, get_rates): self.external_add_task = external_add_task self.config = config self.set_option = set_option self.get_rates = get_rates def got_new_rtt(rtt): self.external_add_task(0, self._inspect_rates, rtt) self.rttmonitor = RTTMonitor(got_new_rtt) self.nodefeeder = NodeFeeder(external_add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = None self.max_samples = 10 # hmm... self.u = SizedList(self.max_samples) self.d = SizedList(self.max_samples) self.t = SizedList(self.max_samples * 10) self.ur = SizedList(self.max_samples) self.dr = SizedList(self.max_samples) self.current_std = 0.001 self.max_std = 0.001 self.max_rates = {} self.max_rates["upload"] = 1.0 self.max_rates["download"] = 1.0 self.max_p = 1.0 self.min_p = 2**500 self.mid_p = ((self.max_p - self.min_p) / 2.0) + self.min_p self.old_p = None
def __init__(self, external_add_task, config, set_option, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_option = set_option self.get_rates = get_rates # Next few lines were added by David Harrison to use RTTMonitor2 #if os.name == 'nt': # icmp_impl = Win32Icmp() #elif os.name == 'posix': # icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): self.external_add_task(0, self._inspect_rates, rtt) #self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.rttmonitor = RTTMonitor(got_new_rtt) self.nodefeeder = NodeFeeder(external_add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = None self.max_samples = 10 # hmm... self.u = SizedList(self.max_samples) self.d = SizedList(self.max_samples) self.t = SizedList(self.max_samples * 10) self.ur = SizedList(self.max_samples) self.dr = SizedList(self.max_samples) self.current_std = 0.001 self.max_std = 0.001 self.max_rates = {} self.max_rates["upload"] = 1.0 self.max_rates["download"] = 1.0 self.max_p = 1.0 self.min_p = 2**500 self.mid_p = ((self.max_p - self.min_p) / 2.0) + self.min_p self.old_p = None # I pulled these numbers out of my ass. if stats: tmp_dir = platform.get_temp_dir() timestr = "%d_%d_%d_%d_%d" % time.localtime()[1:6] stats_dir = os.path.join( tmp_dir, "bittorrent%s_%d" % (timestr, os.getpid()) ) os.mkdir(stats_dir) if debug: print "BandwidthManager: stats_dir = %s" % stats_dir rate_vs_time = os.path.join( stats_dir, "rate_vs_time.plotdata" ) self.rfp = open( rate_vs_time, "w" ) delay_vs_time = os.path.join( stats_dir, "delay_vs_time.plotdata" ) self.dfp = open( delay_vs_time, "w" ) sdev_vs_time = os.path.join( stats_dir, "stddev_vs_time.plotdata" ) self.sdevfp = open( sdev_vs_time, "w" )
def __init__(self, external_add_task, config, set_config, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_config = set_config self.get_rates = get_rates if os.name == 'nt': icmp_impl = Win32Icmp() elif os.name == 'posix': icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): print "got_new_rtt, rtt=", rtt self.external_add_task(0, self._inspect_rates, rtt) self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.nodefeeder = NodeFeeder(add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = bttime() if config['control_law'] == 'aimd': self.control_law = AIMDControlLaw(config['increase_delta'], config['decrease_factor']) elif config['control_law'] == 'aiad': self.control_law = AIADControlLaw(config['increase_delta'], config['decrease_delta']) # This configurability is temporary during testing/tuning. --Dave if config['congestion_estimator'] == "chebyshev": self.congestion_estimator = ChebyshevCongestionEstimator( config['window_size'], config['drop_every_nth'], config['cheby_max_probability'], config['cheby_max_consecutive'], config['cheby_max_threshold'], config['ewma']) elif config['congestion_estimator'] == "variance": self.congestion_estimator = VarianceCongestionEstimator( config['window_size']) elif config['congestion_estimator'] == "vegasish": self.congestion_estimator = VegasishCongestionEstimator( config['window_size'], config['drop_every_nth']) else: raise BTFailure( _("Unrecognized congestion estimator '%s'.") % config['congestion_estimator']) self.starvation_prevention = FixedStarvationPrevention( config['min_upload_rate_limit']) if stats: rlimit_vs_time = \ os.path.join( stats_dir, "rlimit_vs_time.plotdata" ) fp = open(rlimit_vs_time, "w") self.control_law = StreamTracer(self.control_law, fp) _copy_gnuplot("rlimit_vs_time.gnuplot") # samples are max(min_upload_rate_limit,rate). slimit_vs_time = \ os.path.join( stats_dir, "slimit_vs_time.plotdata" ) fp = open(slimit_vs_time, "w") self.starvation_prevention = StreamTracer( self.starvation_prevention, fp) delay_vs_time = os.path.join(stats_dir, "delay_vs_time.plotdata") self.dfp = open(delay_vs_time, "w") _copy_gnuplot("delay_vs_time.gnuplot")
class BandwidthManager(object): """Controls allocation of bandwidth between foreground and background traffic. Currently all BitTorrent traffic is considered background. Background traffic is subjected to a global rate limit that is reduced during congestion to allow foreground traffic to takeover. A 'starvation prevention' building block applies a lower bound. """ def __init__(self, external_add_task, config, set_config, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_config = set_config self.get_rates = get_rates if os.name == 'nt': icmp_impl = Win32Icmp() elif os.name == 'posix': icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): print "got_new_rtt, rtt=", rtt self.external_add_task(0, self._inspect_rates, rtt) self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.nodefeeder = NodeFeeder(add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = bttime() if config['control_law'] == 'aimd': self.control_law = AIMDControlLaw(config['increase_delta'], config['decrease_factor']) elif config['control_law'] == 'aiad': self.control_law = AIADControlLaw(config['increase_delta'], config['decrease_delta']) # This configurability is temporary during testing/tuning. --Dave if config['congestion_estimator'] == "chebyshev": self.congestion_estimator = ChebyshevCongestionEstimator( config['window_size'], config['drop_every_nth'], config['cheby_max_probability'], config['cheby_max_consecutive'], config['cheby_max_threshold'], config['ewma']) elif config['congestion_estimator'] == "variance": self.congestion_estimator = VarianceCongestionEstimator( config['window_size']) elif config['congestion_estimator'] == "vegasish": self.congestion_estimator = VegasishCongestionEstimator( config['window_size'], config['drop_every_nth']) else: raise BTFailure( _("Unrecognized congestion estimator '%s'.") % config['congestion_estimator']) self.starvation_prevention = FixedStarvationPrevention( config['min_upload_rate_limit']) if stats: rlimit_vs_time = \ os.path.join( stats_dir, "rlimit_vs_time.plotdata" ) fp = open(rlimit_vs_time, "w") self.control_law = StreamTracer(self.control_law, fp) _copy_gnuplot("rlimit_vs_time.gnuplot") # samples are max(min_upload_rate_limit,rate). slimit_vs_time = \ os.path.join( stats_dir, "slimit_vs_time.plotdata" ) fp = open(slimit_vs_time, "w") self.starvation_prevention = StreamTracer( self.starvation_prevention, fp) delay_vs_time = os.path.join(stats_dir, "delay_vs_time.plotdata") self.dfp = open(delay_vs_time, "w") _copy_gnuplot("delay_vs_time.gnuplot") #def congestion_estimator_vegas_greg(self, rtt, rate): # # middle_rtt = ((self.propagation_estimator(rtt) + # self.delay_on_full_estimator(rtt)) / 2.0 ) # if t > middle_rtt and c < 0.5: # rate *= 0.5 # if debug: # print type, "down to", rate # else: # rate += 1000 # hmm # if debug: # print type, "up to", rate # return rate #def congestion_estimator_ratio(self, type, t, p, min_p, max_p, rate): # ratio = p / max_p # if debug: # print "RATIO", ratio # if ratio < 0.5: # rate = ratio * self.max_rates[type] # if debug: # print type.upper(), "SET to", rate # else: # rate += rate * (ratio/10.0) # hmm # if debug: # print type.upper(), "UP to", rate # # return max(rate, 1000) #def congestion_estimator_stddev(self, type, std, max_std, rate): # if std > (max_std * 0.80): # FUDGE # rate *= 0.80 # FUDGE # if debug: # print type.upper(), "DOWN to", rate # else: # rate += 1000 # FUDGE # if debug: # print type.upper(), "UP to", rate # # return max(rate, 1000) # FUDGE #def _affect_rate(self, type, std, max_std, rate, set): # rate = self._congestion_estimator_stddev(type, std, max_std, rate) # # rock_bottom = False # if rate <= 1000: # if debug: # print "Rock bottom" # rock_bottom = True # rate = 1000 # # set(int(rate)) # if stats: # print "BandwidthManager._affect_rate(%f)" % rate # self.rfp.write( "%d\t%d\n" % (bttime(),int(rate)) ) # self.sdevfp.write( "%d\t%f\n" % (bttime(), std ) ) # # return rock_bottom def _inspect_rates(self, t=None): """Called whenever an RTT sample arrives. If t == None then a timeout occurred.""" if t == None: t = self.rttmonitor.get_current_rtt() if t == None: # this makes timeouts reduce the maximum std deviation self.congestion_estimator.timeout() return if debug: print "BandwidthManager._inspect_rates: %d" % t if stats: self.dfp.write("%f\t%f\n" % (bttime(), t)) if not self.config['bandwidth_management']: return # TODO: slow start should be smarter than this #if self.start_time < bttime() + 20: # self.config['max_upload_rate'] = 10000000 # self.config['max_dowload_rate'] = 10000000 #if t < 3: # # I simply don't believe you. Go away. # return tup = self.get_rates() if tup == None: return uprate, downrate = tup # proceed through the building blocks. (We can swap in various # implementations of each based on config). is_congested = self.congestion_estimator(t, uprate) rate_limit = self.control_law(is_congested, uprate) rate_limit = self.starvation_prevention(rate_limit) self._set_rate_limit(rate_limit) def _set_rate_limit(self, rate_limit): self.set_config('max_upload_rate', rate_limit)
class BandwidthManager(object): def __init__(self, external_add_task, config, set_option, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_option = set_option self.get_rates = get_rates # Next few lines were added by David Harrison to use RTTMonitor2 #if os.name == 'nt': # icmp_impl = Win32Icmp() #elif os.name == 'posix': # icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): self.external_add_task(0, self._inspect_rates, rtt) #self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.rttmonitor = RTTMonitor(got_new_rtt) self.nodefeeder = NodeFeeder(external_add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = None self.max_samples = 10 # hmm... self.u = SizedList(self.max_samples) self.d = SizedList(self.max_samples) self.t = SizedList(self.max_samples * 10) self.ur = SizedList(self.max_samples) self.dr = SizedList(self.max_samples) self.current_std = 0.001 self.max_std = 0.001 self.max_rates = {} self.max_rates["upload"] = 1.0 self.max_rates["download"] = 1.0 self.max_p = 1.0 self.min_p = 2**500 self.mid_p = ((self.max_p - self.min_p) / 2.0) + self.min_p self.old_p = None # I pulled these numbers out of my ass. if stats: tmp_dir = platform.get_temp_dir() timestr = "%d_%d_%d_%d_%d" % time.localtime()[1:6] stats_dir = os.path.join( tmp_dir, "bittorrent%s_%d" % (timestr, os.getpid()) ) os.mkdir(stats_dir) if debug: print "BandwidthManager: stats_dir = %s" % stats_dir rate_vs_time = os.path.join( stats_dir, "rate_vs_time.plotdata" ) self.rfp = open( rate_vs_time, "w" ) delay_vs_time = os.path.join( stats_dir, "delay_vs_time.plotdata" ) self.dfp = open( delay_vs_time, "w" ) sdev_vs_time = os.path.join( stats_dir, "stddev_vs_time.plotdata" ) self.sdevfp = open( sdev_vs_time, "w" ) def _method_1(self, type, t, c, old_c, rate): # This concept is: # if the correlation is high and the latency is high # then lower the bandwidth limit. # otherwise, raise it. if ((c > 0.96) and (t > 100)): rate /= 2.0 if debug: print type, "down to", rate else: rate += 500 # hmm if debug: print type, "up to", rate return rate def _method_2(self, type, t, c, old_c, rate): # This concept is: # if the correlation is low and the latency is high, lower the limit # otherwise raise it if ((c < 0.60) and (t > 100)): rate /= 2.0 if debug: print type, "down to", rate else: rate += 500 # hmm if debug: print type, "up to", rate return rate def _method_vegasish(self, type, t, c, old_c, rate): middle_rtt = ((self.rttmonitor.get_min_rtt() + self.rttmonitor.get_max_rtt()) / 2.0) if t > middle_rtt: rate *= 1.0/8.0 if debug: print type, "down to", rate else: rate += 1000 # hmm if debug: print type, "up to", rate return rate def _method_vegas_greg(self, type, t, c, old_c, rate): middle_rtt = ((self.rttmonitor.get_min_rtt() + self.rttmonitor.get_max_rtt()) / 2.0) if t > middle_rtt and c < 0.5: rate *= 1.0/8.0 if debug: print type, "down to", rate else: rate += 1000 # hmm if debug: print type, "up to", rate return rate def _method_ratio(self, type, t, p, min_p, max_p, rate): ratio = p / max_p if debug: print "RATIO", ratio if ratio < 0.5: rate = ratio * self.max_rates[type] if debug: print type.upper(), "SET to", rate else: rate += rate * (ratio/10.0) # hmm if debug: print type.upper(), "UP to", rate return max(rate, 1000) def _method_stddev(self, type, std, max_std, rate): if std > (max_std * 0.80): # FUDGE rate *= 0.80 # FUDGE if debug: print type.upper(), "DOWN to", rate else: rate += 1000 # FUDGE if debug: print type.upper(), "UP to", rate return max(rate, 1000) # FUDGE def _affect_rate(self, type, std, max_std, rate, set): rate = self._method_stddev(type, std, max_std, rate) rock_bottom = False if rate <= 4096: if debug: print "Rock bottom" rock_bottom = True rate = 4096 set(int(rate)) if stats: print "BandwidthManager._affect_rate(%f)" % rate self.rfp.write( "%d %d" % (bttime(),int(rate)) ) self.sdevfp.write( "%d %f" % (bttime(), std ) ) return rock_bottom def _inspect_rates(self, t = None): if t == None: t = self.rttmonitor.get_current_rtt() if t == None: # this makes timeouts reduce the maximum std deviation self.max_std *= 0.80 # FUDGE return if self.start_time == None: self.start_time = bttime() if debug: print "BandwidthManager._inspect_rates rtt: %d" % t if stats: self.dfp.write( "%d %d" % (bttime(),t) ) def set_if_enabled(option, value): if not self.config['bandwidth_management']: return if debug: print "Setting %s to: %s" % (option, value) self.set_option(option, value) # TODO: slow start should be smarter than this if self.start_time + 20 > bttime(): if debug: print 'SLOW START', fp(self.start_time + 20), fp(bttime()) set_if_enabled('max_upload_rate', 10000000) set_if_enabled('max_download_rate', 10000000) if t < 3: # I simply don't believe you. Go away. return tup = self.get_rates() if tup == None: return u, d = tup #print "udt", u, d, t #print "uprate, downrate=", u, d self.u.append(u) self.d.append(d) self.t.append(t) self.ur.append(self.config['max_upload_rate']) self.dr.append(self.config['max_download_rate']) #s = time.time() #cu = correlation(self.u, self.t) #cd = correlation(self.d, self.t) #cur = correlation(self.u, self.ur) #cdr = correlation(self.d, self.dr) #e = time.time() self.current_std = standard_deviation(self.t) pu = ratio_sum_lists(self.u, self.t) pd = ratio_sum_lists(self.d, self.t) if len(self.u) > 2: lp = [ x/y for x, y in itertools.izip(self.u, self.t) ] min_pu = min(*lp) max_pu = max(*lp) else: min_pu = u / t max_pu = u / t pu = u / t self.max_rates["upload"] = max(max(self.u), self.max_rates["upload"]) self.max_rates["download"] = max(max(self.d), self.max_rates["download"]) if debug: print 'urate:', fp(u), 'umax:', self.config['max_upload_rate'], \ 'maxstd:', fp(self.max_std), 'std:', fp(self.current_std), \ 'pu:', fp(pu), 'pd:', fp(pd) rb = self._affect_rate("upload", self.current_std, self.max_std, self.config['max_upload_rate'], lambda r : set_if_enabled('max_upload_rate', r)) # don't adjust download rate, it's not nearly correlated enough ## if rb: ## v = int(self.config['max_download_rate'] * 0.90) # FUDGE ## v = max(v, 2000) # FUDGE ## set_if_enabled('max_download_rate', v) ## else: ## v = int(self.config['max_download_rate'] + 6000) # FUDGE ## set_if_enabled('max_download_rate', v) ## if debug: ## print "DOWNLOAD SET to", v #self._affect_rate("download", t, cd, self.last_cd, pd, # self.config['max_download_rate'], # lambda r : self.set_option('max_download_rate', r)) self.max_std = max(self.max_std, self.current_std)
def __init__(self, external_add_task, config, set_option, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_option = set_option self.get_rates = get_rates # Next few lines were added by David Harrison to use RTTMonitor2 #if os.name == 'nt': # icmp_impl = Win32Icmp() #elif os.name == 'posix': # icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): self.external_add_task(0, self._inspect_rates, rtt) #self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.rttmonitor = RTTMonitor(got_new_rtt) self.nodefeeder = NodeFeeder(external_add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = None self.max_samples = 10 # hmm... self.u = SizedList(self.max_samples) self.d = SizedList(self.max_samples) self.t = SizedList(self.max_samples * 2) self.ur = SizedList(self.max_samples) self.dr = SizedList(self.max_samples) self.current_std = 0.001 self.max_std = 0.001 self.last_max = bttime() self.max_rates = {} self.max_rates["upload"] = 1.0 self.max_rates["download"] = 1.0 self.max_p = 1.0 self.min_p = 2**500 self.mid_p = ((self.max_p - self.min_p) / 2.0) + self.min_p self.old_p = None # I pulled these numbers out of my ass. if stats: tmp_dir = platform.get_temp_dir() timestr = "%d_%d_%d_%d_%d" % time.localtime()[1:6] stats_dir = os.path.join( tmp_dir, "bittorrent%s_%d" % (timestr, os.getpid())) os.mkdir(stats_dir) if debug: print "BandwidthManager: stats_dir = %s" % stats_dir rate_vs_time = os.path.join(stats_dir, "rate_vs_time.plotdata") self.rfp = open(rate_vs_time, "w") delay_vs_time = os.path.join(stats_dir, "delay_vs_time.plotdata") self.dfp = open(delay_vs_time, "w") sdev_vs_time = os.path.join(stats_dir, "stddev_vs_time.plotdata") self.sdevfp = open(sdev_vs_time, "w")
class BandwidthManager(object): def __init__(self, external_add_task, config, set_option, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_option = set_option self.get_rates = get_rates # Next few lines were added by David Harrison to use RTTMonitor2 #if os.name == 'nt': # icmp_impl = Win32Icmp() #elif os.name == 'posix': # icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): self.external_add_task(0, self._inspect_rates, rtt) #self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.rttmonitor = RTTMonitor(got_new_rtt) self.nodefeeder = NodeFeeder(external_add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = None self.max_samples = 10 # hmm... self.u = SizedList(self.max_samples) self.d = SizedList(self.max_samples) self.t = SizedList(self.max_samples * 2) self.ur = SizedList(self.max_samples) self.dr = SizedList(self.max_samples) self.current_std = 0.001 self.max_std = 0.001 self.last_max = bttime() self.max_rates = {} self.max_rates["upload"] = 1.0 self.max_rates["download"] = 1.0 self.max_p = 1.0 self.min_p = 2**500 self.mid_p = ((self.max_p - self.min_p) / 2.0) + self.min_p self.old_p = None # I pulled these numbers out of my ass. if stats: tmp_dir = platform.get_temp_dir() timestr = "%d_%d_%d_%d_%d" % time.localtime()[1:6] stats_dir = os.path.join( tmp_dir, "bittorrent%s_%d" % (timestr, os.getpid())) os.mkdir(stats_dir) if debug: print "BandwidthManager: stats_dir = %s" % stats_dir rate_vs_time = os.path.join(stats_dir, "rate_vs_time.plotdata") self.rfp = open(rate_vs_time, "w") delay_vs_time = os.path.join(stats_dir, "delay_vs_time.plotdata") self.dfp = open(delay_vs_time, "w") sdev_vs_time = os.path.join(stats_dir, "stddev_vs_time.plotdata") self.sdevfp = open(sdev_vs_time, "w") def _method_1(self, type, t, c, old_c, rate): # This concept is: # if the correlation is high and the latency is high # then lower the bandwidth limit. # otherwise, raise it. if ((c > 0.96) and (t > 100)): rate /= 2.0 if debug: print type, "down to", rate else: rate += 500 # hmm if debug: print type, "up to", rate return rate def _method_2(self, type, t, c, old_c, rate): # This concept is: # if the correlation is low and the latency is high, lower the limit # otherwise raise it if ((c < 0.60) and (t > 100)): rate /= 2.0 if debug: print type, "down to", rate else: rate += 500 # hmm if debug: print type, "up to", rate return rate def _method_vegasish(self, type, t, c, old_c, rate): middle_rtt = ( (self.rttmonitor.get_min_rtt() + self.rttmonitor.get_max_rtt()) / 2.0) if t > middle_rtt: rate *= 1.0 / 8.0 if debug: print type, "down to", rate else: rate += 1000 # hmm if debug: print type, "up to", rate return rate def _method_vegas_greg(self, type, t, c, old_c, rate): middle_rtt = ( (self.rttmonitor.get_min_rtt() + self.rttmonitor.get_max_rtt()) / 2.0) if t > middle_rtt and c < 0.5: rate *= 1.0 / 8.0 if debug: print type, "down to", rate else: rate += 1000 # hmm if debug: print type, "up to", rate return rate def _method_ratio(self, type, t, p, min_p, max_p, rate): ratio = p / max_p if debug: print "RATIO", ratio if ratio < 0.5: rate = ratio * self.max_rates[type] if debug: print type.upper(), "SET to", rate else: rate += rate * (ratio / 10.0) # hmm if debug: print type.upper(), "UP to", rate return max(rate, 1000) def _method_stddev(self, type, std, max_std, rate): top = 0.80 # FUDGE if std > (max_std * top): center = 1.0 + top - ((1.0 - top) * 0.5) s = min(std / max(0.0001, max_std), center) s = center - s rate *= s if debug: print type.upper(), "DOWN *", s, "to", rate else: s = 1000.0 # FUDGE s *= min(max_std / max(0.0001, std), 4) / 4.0 s = int(s) rate += s if debug: print type.upper(), "UP +", s, "to", rate return max(rate, 1000) # FUDGE def _affect_rate(self, type, std, max_std, rate, set): rate = self._method_stddev(type, std, max_std, rate) rock_bottom = False if rate <= 4096: if debug: print "Rock bottom" rock_bottom = True rate = 4096 set(int(rate)) if stats: print "BandwidthManager._affect_rate(%f)" % rate self.rfp.write("%d %d" % (bttime(), int(rate))) self.sdevfp.write("%d %f" % (bttime(), std)) return rock_bottom def _inspect_rates(self, t=None): if t == None: t = self.rttmonitor.get_current_rtt() if t == None: # this makes timeouts reduce the maximum std deviation self.max_std *= 0.80 # FUDGE return if self.start_time == None: self.start_time = bttime() if debug: print "BandwidthManager._inspect_rates rtt: %d" % t if stats: self.dfp.write("%d %d" % (bttime(), t)) def set_if_enabled(option, value): if not self.config['bandwidth_management']: return if debug: print "Setting %s to: %s" % (option, value) self.set_option(option, value) # TODO: slow start should be smarter than this if self.start_time + 20 > bttime(): if debug: print 'SLOW START', fp(self.start_time + 20), fp(bttime()) set_if_enabled('max_upload_rate', 10000000) set_if_enabled('max_download_rate', 10000000) if t < 3: # I simply don't believe you. Go away. return tup = self.get_rates() if tup == None: return u, d = tup #print "udt", u, d, t #print "uprate, downrate=", u, d self.u.append(u) self.d.append(d) self.t.append(t) self.ur.append(self.config['max_upload_rate']) self.dr.append(self.config['max_download_rate']) #s = time.time() #cu = correlation(self.u, self.t) #cd = correlation(self.d, self.t) #cur = correlation(self.u, self.ur) #cdr = correlation(self.d, self.dr) #e = time.time() self.current_std = standard_deviation(self.t) pu = ratio_sum_lists(self.u, self.t) pd = ratio_sum_lists(self.d, self.t) if len(self.u) > 2: lp = [x / y for x, y in itertools.izip(self.u, self.t)] min_pu = min(*lp) max_pu = max(*lp) else: min_pu = u / t max_pu = u / t pu = u / t self.max_rates["upload"] = max(max(self.u), self.max_rates["upload"]) self.max_rates["download"] = max(max(self.d), self.max_rates["download"]) if debug: print 'urate:', fp(u), 'umax:', self.config['max_upload_rate'], \ 'maxstd:', fp(self.max_std), 'std:', fp(self.current_std), \ 'pu:', fp(pu), 'pd:', fp(pd) rb = self._affect_rate("upload", self.current_std, self.max_std, self.config['max_upload_rate'], lambda r: set_if_enabled('max_upload_rate', r)) # don't adjust download rate, it's not nearly correlated enough ## if rb: ## v = int(self.config['max_download_rate'] * 0.90) # FUDGE ## v = max(v, 2000) # FUDGE ## set_if_enabled('max_download_rate', v) ## else: ## v = int(self.config['max_download_rate'] + 6000) # FUDGE ## set_if_enabled('max_download_rate', v) ## if debug: ## print "DOWNLOAD SET to", v #self._affect_rate("download", t, cd, self.last_cd, pd, # self.config['max_download_rate'], # lambda r : self.set_option('max_download_rate', r)) if self.current_std > self.max_std: self.max_std = self.current_std self.last_max = bttime() elif bttime() - self.last_max > 10: # decay old maximums, to recover from flakey connections self.max_std *= 0.90 # FUDGE self.last_max = bttime()
class BandwidthManager(object): def __init__(self, external_add_task, config, set_option, get_remote_endpoints, get_rates): self.external_add_task = external_add_task self.config = config self.set_option = set_option self.get_rates = get_rates def got_new_rtt(rtt): self.external_add_task(0, self._inspect_rates, rtt) self.rttmonitor = RTTMonitor(got_new_rtt) self.nodefeeder = NodeFeeder(external_add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = None self.max_samples = 10 # hmm... self.u = SizedList(self.max_samples) self.d = SizedList(self.max_samples) self.t = SizedList(self.max_samples * 10) self.ur = SizedList(self.max_samples) self.dr = SizedList(self.max_samples) self.current_std = 0.001 self.max_std = 0.001 self.max_rates = {} self.max_rates["upload"] = 1.0 self.max_rates["download"] = 1.0 self.max_p = 1.0 self.min_p = 2**500 self.mid_p = ((self.max_p - self.min_p) / 2.0) + self.min_p self.old_p = None # I pulled these numbers out of my ass. def _method_1(self, type, t, c, old_c, rate): # This concept is: # if the correlation is high and the latency is high # then lower the bandwidth limit. # otherwise, raise it. if ((c > 0.96) and (t > 100)): rate /= 2.0 if debug: print type, "down to", rate else: rate += 500 # hmm if debug: print type, "up to", rate return rate def _method_2(self, type, t, c, old_c, rate): # This concept is: # if the correlation is low and the latency is high, lower the limit # otherwise raise it if ((c < 0.60) and (t > 100)): rate /= 2.0 if debug: print type, "down to", rate else: rate += 500 # hmm if debug: print type, "up to", rate return rate def _method_vegasish(self, type, t, c, old_c, rate): middle_rtt = ((self.rttmonitor.get_min_rtt() + self.rttmonitor.get_max_rtt()) / 2.0) if t > middle_rtt: rate *= 1.0/8.0 if debug: print type, "down to", rate else: rate += 1000 # hmm if debug: print type, "up to", rate return rate def _method_vegas_greg(self, type, t, c, old_c, rate): middle_rtt = ((self.rttmonitor.get_min_rtt() + self.rttmonitor.get_max_rtt()) / 2.0) if t > middle_rtt and c < 0.5: rate *= 1.0/8.0 if debug: print type, "down to", rate else: rate += 1000 # hmm if debug: print type, "up to", rate return rate def _method_ratio(self, type, t, p, min_p, max_p, rate): ratio = p / max_p if debug: print "RATIO", ratio if ratio < 0.5: rate = ratio * self.max_rates[type] if debug: print type.upper(), "SET to", rate else: rate += rate * (ratio/10.0) # hmm if debug: print type.upper(), "UP to", rate return max(rate, 1000) def _method_stddev(self, type, std, max_std, rate): if std > (max_std * 0.80): # FUDGE rate *= 0.80 # FUDGE if debug: print type.upper(), "DOWN to", rate else: rate += 1000 # FUDGE if debug: print type.upper(), "UP to", rate return max(rate, 1000) # FUDGE def _affect_rate(self, type, std, max_std, rate, set): rate = self._method_stddev(type, std, max_std, rate) rock_bottom = False if rate <= 4096: if debug: print "Rock bottom" rock_bottom = True rate = 4096 set(int(rate)) return rock_bottom def _inspect_rates(self, t = None): if t == None: t = self.rttmonitor.get_current_rtt() if t == None: # this makes timeouts reduce the maximum std deviation self.max_std *= 0.80 # FUDGE return if self.start_time == None: self.start_time = bttime() def set_if_enabled(option, value): if not self.config['bandwidth_management']: return #print "Setting rate to ", value self.set_option(option, value) # TODO: slow start should be smarter than this if self.start_time + 20 > bttime(): set_if_enabled('max_upload_rate', 10000000) set_if_enabled('max_download_rate', 10000000) if t < 3: # I simply don't believe you. Go away. return tup = self.get_rates() if tup == None: return u, d = tup #print "udt", u, d, t #print "uprate, downrate=", u, d self.u.append(u) self.d.append(d) self.t.append(t) self.ur.append(self.config['max_upload_rate']) self.dr.append(self.config['max_download_rate']) #s = time.time() #cu = correlation(self.u, self.t) #cd = correlation(self.d, self.t) #cur = correlation(self.u, self.ur) #cdr = correlation(self.d, self.dr) #e = time.time() self.current_std = standard_deviation(self.t) pu = ratio_sum_lists(self.u, self.t) pd = ratio_sum_lists(self.d, self.t) if len(self.u) > 2: lp = [ x/y for x, y in itertools.izip(self.u, self.t) ] min_pu = min(*lp) max_pu = max(*lp) else: min_pu = u / t max_pu = u / t pu = u / t self.max_rates["upload"] = max(max(self.u), self.max_rates["upload"]) self.max_rates["download"] = max(max(self.d), self.max_rates["download"]) if debug: print "STDDEV", u, self.config['max_upload_rate'], \ self.max_std, self.current_std, pu, pd rb = self._affect_rate("upload", self.current_std, self.max_std, self.config['max_upload_rate'], lambda r : set_if_enabled('max_upload_rate', r)) # don't adjust download rate, it's not nearly correlated enough ## if rb: ## v = int(self.config['max_download_rate'] * 0.90) # FUDGE ## v = max(v, 2000) # FUDGE ## set_if_enabled('max_download_rate', v) ## else: ## v = int(self.config['max_download_rate'] + 6000) # FUDGE ## set_if_enabled('max_download_rate', v) ## if debug: ## print "DOWNLOAD SET to", v #self._affect_rate("download", t, cd, self.last_cd, pd, # self.config['max_download_rate'], # lambda r : self.set_option('max_download_rate', r)) self.max_std = max(self.max_std, self.current_std) #self.last_cu = cu #self.last_cd = cd # we're re-called by the pinging thing #self.external_add_task(0.1, self._inspect_rates)
def __init__(self, external_add_task, config, set_config, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_config = set_config self.get_rates = get_rates if os.name == 'nt': icmp_impl = Win32Icmp() elif os.name == 'posix': icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): print "got_new_rtt, rtt=", rtt self.external_add_task(0, self._inspect_rates, rtt) self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.nodefeeder = NodeFeeder(add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = bttime() if config['control_law'] == 'aimd': self.control_law = AIMDControlLaw(config['increase_delta'], config['decrease_factor']) elif config['control_law'] == 'aiad': self.control_law = AIADControlLaw(config['increase_delta'], config['decrease_delta']) # This configurability is temporary during testing/tuning. --Dave if config['congestion_estimator'] == "chebyshev": self.congestion_estimator = ChebyshevCongestionEstimator( config['window_size'], config['drop_every_nth'], config['cheby_max_probability'], config['cheby_max_consecutive'], config['cheby_max_threshold'], config['ewma']) elif config['congestion_estimator'] == "variance": self.congestion_estimator = VarianceCongestionEstimator( config['window_size']) elif config['congestion_estimator'] == "vegasish": self.congestion_estimator = VegasishCongestionEstimator( config['window_size'], config['drop_every_nth']) else: raise BTFailure(_("Unrecognized congestion estimator '%s'.") % config['congestion_estimator']) self.starvation_prevention = FixedStarvationPrevention( config['min_upload_rate_limit'] ) if stats: rlimit_vs_time = \ os.path.join( stats_dir, "rlimit_vs_time.plotdata" ) fp = open( rlimit_vs_time, "w" ) self.control_law = StreamTracer(self.control_law, fp) _copy_gnuplot( "rlimit_vs_time.gnuplot" ) # samples are max(min_upload_rate_limit,rate). slimit_vs_time = \ os.path.join( stats_dir, "slimit_vs_time.plotdata" ) fp = open( slimit_vs_time, "w" ) self.starvation_prevention = StreamTracer( self.starvation_prevention, fp) delay_vs_time = os.path.join( stats_dir, "delay_vs_time.plotdata" ) self.dfp = open( delay_vs_time, "w" ) _copy_gnuplot( "delay_vs_time.gnuplot" )
class BandwidthManager(object): """Controls allocation of bandwidth between foreground and background traffic. Currently all BitTorrent traffic is considered background. Background traffic is subjected to a global rate limit that is reduced during congestion to allow foreground traffic to takeover. A 'starvation prevention' building block applies a lower bound. """ def __init__(self, external_add_task, config, set_config, get_remote_endpoints, get_rates): if debug: if config['bandwidth_management']: print "bandwidth management is up." else: print "!@#!@#!@#!@#!@# bandwidth management is down." self.external_add_task = external_add_task self.config = config self.set_config = set_config self.get_rates = get_rates if os.name == 'nt': icmp_impl = Win32Icmp() elif os.name == 'posix': icmp_impl = UnixIcmp(external_add_task, config['xicmp_port']) def got_new_rtt(rtt): print "got_new_rtt, rtt=", rtt self.external_add_task(0, self._inspect_rates, rtt) self.rttmonitor = RTTMonitor(got_new_rtt, icmp_impl) self.nodefeeder = NodeFeeder(add_task=external_add_task, get_remote_endpoints=get_remote_endpoints, rttmonitor=self.rttmonitor) self.start_time = bttime() if config['control_law'] == 'aimd': self.control_law = AIMDControlLaw(config['increase_delta'], config['decrease_factor']) elif config['control_law'] == 'aiad': self.control_law = AIADControlLaw(config['increase_delta'], config['decrease_delta']) # This configurability is temporary during testing/tuning. --Dave if config['congestion_estimator'] == "chebyshev": self.congestion_estimator = ChebyshevCongestionEstimator( config['window_size'], config['drop_every_nth'], config['cheby_max_probability'], config['cheby_max_consecutive'], config['cheby_max_threshold'], config['ewma']) elif config['congestion_estimator'] == "variance": self.congestion_estimator = VarianceCongestionEstimator( config['window_size']) elif config['congestion_estimator'] == "vegasish": self.congestion_estimator = VegasishCongestionEstimator( config['window_size'], config['drop_every_nth']) else: raise BTFailure(_("Unrecognized congestion estimator '%s'.") % config['congestion_estimator']) self.starvation_prevention = FixedStarvationPrevention( config['min_upload_rate_limit'] ) if stats: rlimit_vs_time = \ os.path.join( stats_dir, "rlimit_vs_time.plotdata" ) fp = open( rlimit_vs_time, "w" ) self.control_law = StreamTracer(self.control_law, fp) _copy_gnuplot( "rlimit_vs_time.gnuplot" ) # samples are max(min_upload_rate_limit,rate). slimit_vs_time = \ os.path.join( stats_dir, "slimit_vs_time.plotdata" ) fp = open( slimit_vs_time, "w" ) self.starvation_prevention = StreamTracer( self.starvation_prevention, fp) delay_vs_time = os.path.join( stats_dir, "delay_vs_time.plotdata" ) self.dfp = open( delay_vs_time, "w" ) _copy_gnuplot( "delay_vs_time.gnuplot" ) #def congestion_estimator_vegas_greg(self, rtt, rate): # # middle_rtt = ((self.propagation_estimator(rtt) + # self.delay_on_full_estimator(rtt)) / 2.0 ) # if t > middle_rtt and c < 0.5: # rate *= 0.5 # if debug: # print type, "down to", rate # else: # rate += 1000 # hmm # if debug: # print type, "up to", rate # return rate #def congestion_estimator_ratio(self, type, t, p, min_p, max_p, rate): # ratio = p / max_p # if debug: # print "RATIO", ratio # if ratio < 0.5: # rate = ratio * self.max_rates[type] # if debug: # print type.upper(), "SET to", rate # else: # rate += rate * (ratio/10.0) # hmm # if debug: # print type.upper(), "UP to", rate # # return max(rate, 1000) #def congestion_estimator_stddev(self, type, std, max_std, rate): # if std > (max_std * 0.80): # FUDGE # rate *= 0.80 # FUDGE # if debug: # print type.upper(), "DOWN to", rate # else: # rate += 1000 # FUDGE # if debug: # print type.upper(), "UP to", rate # # return max(rate, 1000) # FUDGE #def _affect_rate(self, type, std, max_std, rate, set): # rate = self._congestion_estimator_stddev(type, std, max_std, rate) # # rock_bottom = False # if rate <= 1000: # if debug: # print "Rock bottom" # rock_bottom = True # rate = 1000 # # set(int(rate)) # if stats: # print "BandwidthManager._affect_rate(%f)" % rate # self.rfp.write( "%d\t%d\n" % (bttime(),int(rate)) ) # self.sdevfp.write( "%d\t%f\n" % (bttime(), std ) ) # # return rock_bottom def _inspect_rates(self, t = None): """Called whenever an RTT sample arrives. If t == None then a timeout occurred.""" if t == None: t = self.rttmonitor.get_current_rtt() if t == None: # this makes timeouts reduce the maximum std deviation self.congestion_estimator.timeout() return if debug: print "BandwidthManager._inspect_rates: %d" % t if stats: self.dfp.write( "%f\t%f\n" % (bttime(),t) ) if not self.config['bandwidth_management']: return # TODO: slow start should be smarter than this #if self.start_time < bttime() + 20: # self.config['max_upload_rate'] = 10000000 # self.config['max_dowload_rate'] = 10000000 #if t < 3: # # I simply don't believe you. Go away. # return tup = self.get_rates() if tup == None: return uprate, downrate = tup # proceed through the building blocks. (We can swap in various # implementations of each based on config). is_congested = self.congestion_estimator(t,uprate) rate_limit = self.control_law(is_congested,uprate) rate_limit = self.starvation_prevention(rate_limit) self._set_rate_limit(rate_limit) def _set_rate_limit(self,rate_limit): self.set_config('max_upload_rate', rate_limit)