diff --git a/examples/cpu.py b/examples/cpu.py index 2af7d67..e0479b5 100755 --- a/examples/cpu.py +++ b/examples/cpu.py @@ -2,17 +2,40 @@ """ cpu.py: test iperf bandwidth for varying cpu limits -""" +Since we are limiting the hosts (only), we should expect the iperf +processes to be affected, as well as any system processing which is +billed to the hosts. + +We reserve >50% of cycles for system processing; we assume that +this is enough for it not to affect results. Hosts are limited to +40% of total cycles, which we assume is enough to make them CPU +bound. + +As CPU performance increases over time, we may have to reduce the +overall CPU allocation so that the host processing is still CPU bound. +This is perhaps an argument for specifying performance in a more +system-independent manner. + +It would also be nice to have a better handle on limiting packet +processing cycles. It's not entirely clear to me how those are +billed to user or system processes if we are using OVS with a kernel +datapath. With a user datapath, they are easier to account for, but +overall performance is usually lower. + +Although the iperf client uses more CPU and should be CPU bound (?), +we measure the received data at the server since the client transmit +rate includes buffering. +""" from mininet.net import Mininet from mininet.node import CPULimitedHost from mininet.topolib import TreeTopo -from mininet.util import custom, waitListening +from mininet.util import custom, waitListening, pmonitor from mininet.log import setLogLevel, info -def bwtest( cpuLimits, period_us=100000, seconds=5 ): +def bwtest( cpuLimits, period_us=100000, seconds=10 ): """Example/test of link and CPU bandwidth limits cpu: cpu limit as fraction of overall CPU time""" @@ -23,25 +46,32 @@ def bwtest( cpuLimits, period_us=100000, seconds=5 ): for sched in 'rt', 'cfs': info( '*** Testing with', sched, 'bandwidth limiting\n' ) for cpu in cpuLimits: + # cpu is the cpu fraction for all hosts, so we divide + # it across two hosts host = custom( CPULimitedHost, sched=sched, period_us=period_us, - cpu=cpu ) + cpu=.5*cpu ) try: net = Mininet( topo=topo, host=host ) # pylint: disable=bare-except except: - info( '*** Skipping host %s\n' % sched ) + info( '*** Skipping scheduler %s\n' % sched ) break net.start() net.pingAll() hosts = [ net.getNodeByName( h ) for h in topo.hosts() ] client, server = hosts[ 0 ], hosts[ -1 ] - server.cmd( 'iperf -s -p 5001 &' ) + info( '*** Starting iperf with %d%% of CPU allocated to hosts\n' % + ( 100.0 * cpu ) ) + # We measure at the server because it doesn't include + # the client's buffer fill rate + popen = server.popen( 'iperf -yc -s -p 5001' ) waitListening( client, server, 5001 ) - result = client.cmd( 'iperf -yc -t %s -c %s' % ( - seconds, server.IP() ) ).split( ',' ) + popen.stdout.readline() # ignore empty result from waitListening/telnet + client.cmd( 'iperf -yc -t %s -c %s' % ( seconds, server.IP() ) ) + result = popen.stdout.readline().split( ',' ) bps = float( result[ -1 ] ) - server.cmdPrint( 'kill %iperf' ) + popen.terminate() net.stop() updated = results.get( sched, [] ) updated += [ ( cpu, bps ) ] @@ -55,18 +85,21 @@ def dump( results ): fmt = '%s\t%s\t%s\n' - info( '\n', fmt % ( 'sched', 'cpu', 'client MB/s' ) ) + info( '\n' ) + info( fmt % ( 'sched', 'cpu', 'received bits/sec' ) ) for sched in sorted( results.keys() ): entries = results[ sched ] for cpu, bps in entries: - pct = '%.2f%%' % ( cpu * 100 ) - mbps = bps / 1e6 + pct = '%d%%' % ( cpu * 100 ) + mbps = '%.2e' % bps info( fmt % ( sched, pct, mbps ) ) if __name__ == '__main__': setLogLevel( 'info' ) - limits = [ .45, .4, .3, .2, .1 ] + # These are the limits for the hosts/iperfs - the + # rest is for system processes + limits = [ .5, .4, .3, .2, .1 ] out = bwtest( limits ) dump( out ) diff --git a/examples/test/test_cpu.py b/examples/test/test_cpu.py index 1534743..c8b9e58 100755 --- a/examples/test/test_cpu.py +++ b/examples/test/test_cpu.py @@ -5,13 +5,12 @@ Test for cpu.py results format: - sched cpu client MB/s - - cfs 45.00% 13254.669841 - cfs 40.00% 11822.441399 - cfs 30.00% 5112.963009 - cfs 20.00% 3449.090009 - cfs 10.00% 2271.741564 +sched cpu received bits/sec +cfs 50% 8.14e+09 +cfs 40% 6.48e+09 +cfs 30% 4.56e+09 +cfs 20% 2.84e+09 +cfs 10% 1.29e+09 """ @@ -28,7 +27,7 @@ class testCPU( unittest.TestCase ): "Verify that CPU utilization is monotonically decreasing for each scheduler" p = pexpect.spawn( 'python -m mininet.examples.cpu' ) # matches each line from results( shown above ) - opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.]+)', + opts = [ '([a-z]+)\t([\d\.]+)%\t([\d\.e\+]+)', pexpect.EOF ] scheds = [] while True: