3636LATENCY_PING_INTERVAL = 0.1
3737LATENCY_DURATION = 2.0
3838
39+ BANDWIDTH_PACKET_SIZE = 1024
40+ BANDWIDTH_DURATION = 2.0
41+
3942
4043def task_pidigits ():
4144 """Pi calculation (Python)"""
@@ -149,6 +152,7 @@ def compute(s):
149152 throughput_tasks .append (task_compress_zlib )
150153
151154latency_tasks = throughput_tasks
155+ bandwidth_tasks = [task_pidigits ]
152156
153157
154158class TimedLoop :
@@ -394,6 +398,133 @@ def run_latency_tests(max_threads):
394398 print ()
395399
396400
401+ BW_END = "END"
402+
403+ def bandwidth_client (addr , packet_size , duration ):
404+ sock = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
405+ sock .bind (("127.0.0.1" , 0 ))
406+ local_addr = sock .getsockname ()
407+ _time = time .time
408+ _sleep = time .sleep
409+ def _send_chunk (msg ):
410+ _sendto (sock , ("%r#%s\n " % (local_addr , msg )).rjust (packet_size ), addr )
411+ # We give the parent some time to be ready.
412+ _sleep (1.0 )
413+ try :
414+ start_time = _time ()
415+ end_time = start_time + duration * 2.0
416+ i = 0
417+ while _time () < end_time :
418+ _send_chunk (str (i ))
419+ s = _recv (sock , packet_size )
420+ assert len (s ) == packet_size
421+ i += 1
422+ _send_chunk (BW_END )
423+ finally :
424+ sock .close ()
425+
426+ def run_bandwidth_client (** kwargs ):
427+ cmd_line = [sys .executable , '-E' , os .path .abspath (__file__ )]
428+ cmd_line .extend (['--bwclient' , repr (kwargs )])
429+ return subprocess .Popen (cmd_line ) #, stdin=subprocess.PIPE,
430+ #stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
431+
432+ def run_bandwidth_test (func , args , nthreads ):
433+ # Create a listening socket to receive the packets. We use UDP which should
434+ # be painlessly cross-platform.
435+ sock = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
436+ sock .bind (("127.0.0.1" , 0 ))
437+ addr = sock .getsockname ()
438+
439+ duration = BANDWIDTH_DURATION
440+ packet_size = BANDWIDTH_PACKET_SIZE
441+
442+ results = []
443+ threads = []
444+ end_event = []
445+ start_cond = threading .Condition ()
446+ started = False
447+ if nthreads > 0 :
448+ # Warm up
449+ func (* args )
450+
451+ results = []
452+ loop = TimedLoop (func , args )
453+ ready = []
454+ ready_cond = threading .Condition ()
455+
456+ def run ():
457+ with ready_cond :
458+ ready .append (None )
459+ ready_cond .notify ()
460+ with start_cond :
461+ while not started :
462+ start_cond .wait ()
463+ loop (start_time , duration * 1.5 , end_event , do_yield = False )
464+
465+ for i in range (nthreads ):
466+ threads .append (threading .Thread (target = run ))
467+ for t in threads :
468+ t .setDaemon (True )
469+ t .start ()
470+ # Wait for threads to be ready
471+ with ready_cond :
472+ while len (ready ) < nthreads :
473+ ready_cond .wait ()
474+
475+ # Run the client and wait for the first packet to arrive before
476+ # unblocking the background threads.
477+ process = run_bandwidth_client (addr = addr ,
478+ packet_size = packet_size ,
479+ duration = duration )
480+ _time = time .time
481+ # This will also wait for the parent to be ready
482+ s = _recv (sock , packet_size )
483+ remote_addr = eval (s .partition ('#' )[0 ])
484+
485+ with start_cond :
486+ start_time = _time ()
487+ started = True
488+ start_cond .notify (nthreads )
489+
490+ n = 0
491+ first_time = None
492+ while not end_event and BW_END not in s :
493+ _sendto (sock , s , remote_addr )
494+ s = _recv (sock , packet_size )
495+ if first_time is None :
496+ first_time = _time ()
497+ n += 1
498+ end_time = _time ()
499+
500+ end_event .append (None )
501+ for t in threads :
502+ t .join ()
503+ process .kill ()
504+
505+ return (n - 1 ) / (end_time - first_time )
506+
507+ def run_bandwidth_tests (max_threads ):
508+ for task in bandwidth_tasks :
509+ print ("Background CPU task:" , task .__doc__ )
510+ print ()
511+ func , args = task ()
512+ nthreads = 0
513+ baseline_speed = None
514+ while nthreads <= max_threads :
515+ results = run_bandwidth_test (func , args , nthreads )
516+ speed = results
517+ #speed = len(results) * 1.0 / results[-1][0]
518+ print ("CPU threads=%d: %.1f" % (nthreads , speed ), end = "" )
519+ if baseline_speed is None :
520+ print (" packets/s." )
521+ baseline_speed = speed
522+ else :
523+ print (" ( %d %%)" % (speed / baseline_speed * 100 ))
524+ nthreads += 1
525+ print ()
526+
527+
397528def main ():
398529 usage = "usage: %prog [-h|--help] [options]"
399530 parser = OptionParser (usage = usage )
@@ -403,6 +534,9 @@ def main():
403534 parser .add_option ("-l" , "--latency" ,
404535 action = "store_true" , dest = "latency" , default = False ,
405536 help = "run latency tests" )
537+ parser .add_option ("-b" , "--bandwidth" ,
538+ action = "store_true" , dest = "bandwidth" , default = False ,
539+ help = "run I/O bandwidth tests" )
406540 parser .add_option ("-i" , "--interval" ,
407541 action = "store" , type = "int" , dest = "check_interval" , default = None ,
408542 help = "sys.setcheckinterval() value" )
@@ -413,10 +547,13 @@ def main():
413547 action = "store" , type = "int" , dest = "nthreads" , default = 4 ,
414548 help = "max number of threads in tests" )
415549
416- # Hidden option to run the pinging client
550+ # Hidden option to run the pinging and bandwidth clients
417551 parser .add_option ("" , "--latclient" ,
418552 action = "store" , dest = "latclient" , default = None ,
419553 help = SUPPRESS_HELP )
554+ parser .add_option ("" , "--bwclient" ,
555+ action = "store" , dest = "bwclient" , default = None ,
556+ help = SUPPRESS_HELP )
420557
421558 options , args = parser .parse_args ()
422559 if args :
@@ -427,8 +564,13 @@ def main():
427564 latency_client (** kwargs )
428565 return
429566
430- if not options .throughput and not options .latency :
431- options .throughput = options .latency = True
567+ if options .bwclient :
568+ kwargs = eval (options .bwclient )
569+ bandwidth_client (** kwargs )
570+ return
571+
572+ if not options .throughput and not options .latency and not options .bandwidth :
573+ options .throughput = options .latency = options .bandwidth = True
432574 if options .check_interval :
433575 sys .setcheckinterval (options .check_interval )
434576 if options .switch_interval :
@@ -458,5 +600,10 @@ def main():
458600 print ()
459601 run_latency_tests (options .nthreads )
460602
603+ if options .bandwidth :
604+ print ("--- I/O bandwidth ---" )
605+ print ()
606+ run_bandwidth_tests (options .nthreads )
607+
461608if __name__ == "__main__" :
462609 main ()
0 commit comments