Added simple all-to-all UDP bandwidth test.
This commit is contained in:
@@ -0,0 +1,200 @@
|
|||||||
|
/* udpbwtest: a simple bandwidth test
|
||||||
|
*
|
||||||
|
* To test all-to-all communication, we simply open up a
|
||||||
|
* UDP socket and repeat the following:
|
||||||
|
*
|
||||||
|
* 1. Send a packet to a random host if possible
|
||||||
|
* 2. Receive a packet if possible
|
||||||
|
* 3. Periodically report our I/O bandwidth
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
enum { Port=12345 };
|
||||||
|
|
||||||
|
/* Handy utility functions */
|
||||||
|
|
||||||
|
int bindAddr( int fd, long addr, u_short port ) {
|
||||||
|
/* Easy interface to bind */
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
bzero( &sa, sizeof( sa ) );
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_addr.s_addr = htonl( addr );
|
||||||
|
sa.sin_port = htons( port );
|
||||||
|
return bind( fd, ( struct sockaddr * )&sa, sizeof( sa ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
int udpSocket( u_short port ) {
|
||||||
|
/* Easy interface to making a UDP socket */
|
||||||
|
int fd = socket( AF_INET, SOCK_DGRAM, 0 );
|
||||||
|
if ( fd < 0 ) { return fd; }
|
||||||
|
int err = bindAddr( fd, INADDR_ANY, port );
|
||||||
|
if ( err < 0 ) { return err; }
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t sendBytes( int sock, char *outbuf, size_t bufsize,
|
||||||
|
struct in_addr *addr, u_short port ) {
|
||||||
|
/* Simpler sendto */
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
int err;
|
||||||
|
bzero( &sa, sizeof( sa ) );
|
||||||
|
sa.sin_family = AF_INET;
|
||||||
|
sa.sin_addr = *addr;
|
||||||
|
sa.sin_port = htons( port );
|
||||||
|
err = sendto( sock, outbuf, bufsize, 0, (const struct sockaddr *) &sa,
|
||||||
|
sizeof( sa ) );
|
||||||
|
if ( err < 0 ) { perror( "sendto:" ); }
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t recvBytes( int sock, char *inbuf, size_t bufsize,
|
||||||
|
struct in_addr *addr) {
|
||||||
|
/* Simpler recvfrom */
|
||||||
|
struct sockaddr_in sa;
|
||||||
|
socklen_t saLen = sizeof( sa );
|
||||||
|
ssize_t len;
|
||||||
|
len = recvfrom( sock, inbuf, bufsize, 0, (struct sockaddr *) &sa, &saLen );
|
||||||
|
if ( len >= 0 ) {
|
||||||
|
assert( saLen == sizeof( sa ) );
|
||||||
|
*addr = sa.sin_addr;
|
||||||
|
}
|
||||||
|
else { perror( "recvfrom:" ); }
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int readable( int fd ) {
|
||||||
|
/* Poll a single file descriptor for reading */
|
||||||
|
struct pollfd fds = { fd, POLLIN, POLLIN };
|
||||||
|
int result = poll( &fds, 1, 0 );
|
||||||
|
/* True if there is one readable descriptor */
|
||||||
|
return ( result == 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int writable( int fd ) {
|
||||||
|
/* Poll a single file descriptor for writing */
|
||||||
|
struct pollfd fds = { fd, POLLOUT, POLLOUT };
|
||||||
|
int result = poll( &fds, 1, 0 );
|
||||||
|
/* True if there is one writable descriptor */
|
||||||
|
return ( result == 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int poll1( int fd, int flags, int ms ) {
|
||||||
|
/* Call poll on a single descriptor */
|
||||||
|
struct pollfd fds[] = { { fd, flags, 0 } };
|
||||||
|
return poll( fds, 1, ms );
|
||||||
|
}
|
||||||
|
|
||||||
|
int waitReadable( int fd, int ms ) { return poll1( fd, POLLIN, ms ); }
|
||||||
|
int waitWritable( int fd, int ms ) { return poll1( fd, POLLIN, ms ); }
|
||||||
|
int waitReadableOrWritable( int fd, int ms ) {
|
||||||
|
return poll1( fd, POLLIN | POLLOUT, ms );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Timer support */
|
||||||
|
|
||||||
|
int alarm = 0;
|
||||||
|
void handleAlarm( int sig) { alarm = 1; }
|
||||||
|
|
||||||
|
void startTimer( int seconds) {
|
||||||
|
struct itimerval v;
|
||||||
|
v.it_interval.tv_sec = seconds;
|
||||||
|
v.it_interval.tv_usec = 0;
|
||||||
|
v.it_value = v.it_interval;
|
||||||
|
signal( SIGALRM, handleAlarm );
|
||||||
|
setitimer( ITIMER_REAL, &v, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void startOneSecondTimer() { startTimer( 1 ); }
|
||||||
|
|
||||||
|
void stopTimer() {
|
||||||
|
struct itimerval v;
|
||||||
|
bzero( &v, sizeof v );
|
||||||
|
setitimer( ITIMER_REAL, &v, 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual program */
|
||||||
|
|
||||||
|
void bwtest( int sock, struct in_addr *hosts, int hostCount ) {
|
||||||
|
|
||||||
|
/* Test our bandwidth, by receiving whatever we get, and sending
|
||||||
|
* randomly to a set of hosts */
|
||||||
|
|
||||||
|
char outbuf[ 1024 ];
|
||||||
|
char inbuf[ 1024 ];
|
||||||
|
time_t seconds = time( 0 );
|
||||||
|
uint64_t b, bytes;
|
||||||
|
uint64_t inbytes = 0, outbytes = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for ( i = 0; i < sizeof( outbuf ); i++ ) { outbuf[ i ] = i % 255; }
|
||||||
|
|
||||||
|
while ( 1 ) {
|
||||||
|
size_t addr_len;
|
||||||
|
struct in_addr addr = hosts[ random() % hostCount ];
|
||||||
|
/* Wait until we have something to do. */
|
||||||
|
waitReadableOrWritable( sock, 0 );
|
||||||
|
/* Receive some bytes */
|
||||||
|
for ( b = 0; b < 10 && readable( sock ); b += 1 ) {
|
||||||
|
bytes = recvBytes( sock, inbuf, sizeof( inbuf ), &addr );
|
||||||
|
inbytes += bytes;
|
||||||
|
}
|
||||||
|
/* Send some bytes */
|
||||||
|
for ( b = 0; b < 10 && writable(sock); b += 1 ) {
|
||||||
|
bytes = sendBytes( sock, outbuf, sizeof( outbuf ),
|
||||||
|
&addr, Port );
|
||||||
|
outbytes += bytes;
|
||||||
|
}
|
||||||
|
/* Periodically report bandwidth */
|
||||||
|
if ( alarm ) {
|
||||||
|
alarm = 0;
|
||||||
|
seconds++;
|
||||||
|
printf("%d s: in %.2f Mbps, out %.2f Mbps\n",
|
||||||
|
seconds, 8.0*inbytes/1e6, 8.0*outbytes/1e6 ); fflush( stdout );
|
||||||
|
inbytes = outbytes = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main( int argc, char *argv[] ) {
|
||||||
|
|
||||||
|
struct in_addr start, *hosts, addr;
|
||||||
|
int count, sock, i;
|
||||||
|
|
||||||
|
if ( argc < 2 ) {
|
||||||
|
fprintf( stderr, "usage: %s host...\n", argv[ 0 ] );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
count = argc - 1;
|
||||||
|
hosts = (struct in_addr *) malloc( count * sizeof( struct in_addr ) );
|
||||||
|
if ( hosts == NULL ) { perror( "malloc:" ); exit( 1 ); }
|
||||||
|
|
||||||
|
for ( i = 0; i < count; i++ )
|
||||||
|
inet_aton( argv[ i + 1 ], &hosts[ i ] );
|
||||||
|
|
||||||
|
sock = udpSocket( Port );
|
||||||
|
if ( sock < 0 ) { perror( "udpSocket:" ); exit( 1 ); }
|
||||||
|
|
||||||
|
startOneSecondTimer();
|
||||||
|
|
||||||
|
bwtest( sock, hosts, count ); /* Never returns for now... */
|
||||||
|
|
||||||
|
stopTimer();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Executable
+101
@@ -0,0 +1,101 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
"""
|
||||||
|
Create a tree network and run udpbwtest.c on it, attempting to
|
||||||
|
saturate global bandwidth by sending constant all-to-all
|
||||||
|
udp traffic. This should be something of a stress test.
|
||||||
|
|
||||||
|
We should also make a tcp version. :D
|
||||||
|
"""
|
||||||
|
|
||||||
|
import select, sys, time, re
|
||||||
|
from mininet import init, TreeNet, Cli, flush, quietRun
|
||||||
|
|
||||||
|
# Some useful stuff: buffered readline and host monitoring
|
||||||
|
|
||||||
|
def readline( host, buffer ):
|
||||||
|
"Read a line from a host, buffering with buffer."
|
||||||
|
buffer += host.read( 1024 )
|
||||||
|
if '\n' not in buffer: return None, buffer
|
||||||
|
pos = buffer.find( '\n' )
|
||||||
|
line = buffer[ 0 : pos ]
|
||||||
|
rest = buffer[ pos + 1 :]
|
||||||
|
return line, rest
|
||||||
|
|
||||||
|
def monitor( hosts, seconds ):
|
||||||
|
"Monitor a set of hosts and yield their output."
|
||||||
|
poller = select.poll()
|
||||||
|
Node = hosts[ 0 ] # so we can call class method
|
||||||
|
buffers = {}
|
||||||
|
for host in hosts:
|
||||||
|
poller.register( host.stdout )
|
||||||
|
buffers[ host ] = ''
|
||||||
|
quitTime = time.time() + seconds
|
||||||
|
while time.time() < quitTime:
|
||||||
|
ready = poller.poll()
|
||||||
|
for fd, event in ready:
|
||||||
|
host = Node.fdToNode( fd )
|
||||||
|
line, buffers[ host ] = readline( host, buffers[ host ] )
|
||||||
|
if line: yield host, line
|
||||||
|
yield None, ''
|
||||||
|
|
||||||
|
# bwtest support
|
||||||
|
|
||||||
|
def parsebwtest( line,
|
||||||
|
r=re.compile( r'(\d+) s: in ([\d\.]+) Mbps, out ([\d\.]+) Mbps' ) ):
|
||||||
|
match = r.match( line )
|
||||||
|
return match.group( 1, 2, 3 ) if match else ( None, None, None )
|
||||||
|
|
||||||
|
def printTotalHeader():
|
||||||
|
print
|
||||||
|
print "time(s)\thosts\ttotal in/out (Mbps)\tavg in/out (Mbps)"
|
||||||
|
|
||||||
|
def printTotal( time=None, result=None ):
|
||||||
|
intotal = outtotal = 0.0
|
||||||
|
count = len( result )
|
||||||
|
for host, inbw, outbw in result:
|
||||||
|
intotal += inbw
|
||||||
|
outtotal += outbw
|
||||||
|
inavg = intotal / count if count > 0 else 0
|
||||||
|
outavg = outtotal / count if count > 0 else 0
|
||||||
|
print '%d\t%d\t%.2f/%.2f\t\t%.2f/%.2f' % ( time, count, intotal, outtotal,
|
||||||
|
inavg, outavg )
|
||||||
|
|
||||||
|
def udpbwtest( controllers, switches, hosts, seconds ):
|
||||||
|
"Start up and monitor udpbwtest on each of our hosts."
|
||||||
|
hostCount = len( hosts )
|
||||||
|
print "*** Starting udpbwtest on hosts"
|
||||||
|
for host in hosts:
|
||||||
|
ips = [ h.IP() for h in hosts if h != host ]
|
||||||
|
print host.name, ; flush()
|
||||||
|
host.cmd( './udpbwtest ' + ' '.join( ips ) + ' &' )
|
||||||
|
print
|
||||||
|
results = {}
|
||||||
|
print "*** Monitoring hosts"
|
||||||
|
output = monitor( hosts, seconds )
|
||||||
|
while True:
|
||||||
|
host, line = output.next()
|
||||||
|
if host is None: break
|
||||||
|
time, inbw, outbw = parsebwtest( line )
|
||||||
|
if time is not None:
|
||||||
|
time, inbw, outbw = int( time ), float( inbw ), float( outbw )
|
||||||
|
result = results.get( time, [] ) + [ ( host, inbw, outbw ) ]
|
||||||
|
if len( result ) == hostCount: printTotal( time, result )
|
||||||
|
results[ time ] = result
|
||||||
|
print "*** Stopping udpbwtest processes"
|
||||||
|
# We *really* don't want these things hanging around!
|
||||||
|
quietRun( 'killall -9 udpbwtest' )
|
||||||
|
print
|
||||||
|
print "*** Results:"
|
||||||
|
printTotalHeader()
|
||||||
|
times = sorted( results.keys() )
|
||||||
|
for time in times:
|
||||||
|
printTotal( time - times[ 0 ] , results[ time ] )
|
||||||
|
print
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
init()
|
||||||
|
network = TreeNet( depth=2, fanout=8, kernel=True )
|
||||||
|
def test( c, s, h ): return udpbwtest( c, s, h, seconds=10 )
|
||||||
|
network.run( test )
|
||||||
|
|
||||||
Reference in New Issue
Block a user