/* Synthetic workload model for Tumblr network traffic */ /* */ /* Written by Carey Williamson, University of Calgary */ /* */ /* Usage: cc -o tumblr tumblr.c -lm */ /* ./tumblr */ #include #include #include #include /* Tumblr model parameters for synthetic workload generation */ #define NUM_SESSIONS 2500 #define MEAN_SESSION_IAT 120.0 /* about one every 2 minutes */ /* #define FIXED_ARRIVAL_RATE 1 */ #define MEAN_CONV 5.0 #define MEAN_CONN 10.0 #define WEB_PROB 0.7 #define MEDIA_PROB 0.2 #define BOTH_PROB 0.1 #define TAIL_PROB 0.75 #define SENT_BYTES_LOG_MEAN 12.0 #define SENT_BYTES_LOG_STD 2.0 #define RECD_BYTES_LOG_MEAN 14.0 #define RECD_BYTES_LOG_STD 2.0 #define TRUNCATE 1 #define UPLOAD_BANDWIDTH 1500000.0 /* 1.5 Mbps */ #define DOWNLOAD_BANDWIDTH 3000000.0 /* 3.0 Mbps */ #define PERSISTENT_PROB 0.2 #define CONN_TIMEOUT 60.0 #define CONV_GAP 240.0 #define MIN_PORT 49152 #define MAX_PORT 65535 #define ONE_DAY 86400 #define END_OF_TIME 999999.9 /* Debugging output */ /* #define DEBUG 1 */ /* #define DEBUG2 1 */ /***********************************************************************/ /* RANDOM NUMBER GENERATION STUFF */ /***********************************************************************/ /* Parameters for random number generation. */ #define MAX_INT 2147483647 /* Maximum positive integer 2^31 - 1 */ /* array used for initializing seeds for Normal distribution */ unsigned short Nvar1_seed[3] = {4,7,2}; unsigned short Nvar2_seed[3] = {5,8,1}; unsigned short Nvar3_seed[3] = {9,6,3}; /* Generate a random floating point value uniformly distributed in [0,1] */ float Uniform01() { float randnum; /* get a random positive integer from random() */ randnum = (float) 1.0 * random(); /* divide by max int to get something in 0..1 */ randnum = (float) randnum / (1.0 * MAX_INT); return( randnum ); } /* Generate a random floating point number from an exponential */ /* distribution with mean mu. */ float Exponential(mu) float mu; { float randnum, ans; randnum = Uniform01(); ans = -(mu) * log(randnum); return( ans ); } /* Generate random positive integer geometrically distributed with mean m */ int Geometric(m) double m; { double p; /* p is the probability of "success" for each trial */ int k; k = 1; p = 1.0 / m; /* the inverse of the mean */ while( Uniform01() > p ) k++; return( k ); } /* the rejection method is used for normal distribution (a) Generate two uniform U(0,1) variates u1 and u2. (b) Let x = -log(u1). (c) If u2 > e to the power -(x - 1)square by 2, go back to step [a]. (d) Generate u3. (e) If u3 > 0.5, return (mean + (std * x)); otherwise return (mean - (std * x)). */ double Normal(float mean, float std) { double var_u1, var_u2, var_u3; double x, tmp_val; var_u1 = erand48(Nvar1_seed); var_u2 = erand48(Nvar2_seed); x = -log(var_u1); tmp_val = exp(-((x - 1)*(x - 1)) / 2); while( var_u2 > tmp_val ) { var_u1 = erand48(Nvar1_seed); var_u2 = erand48(Nvar2_seed); x = -log(var_u1); tmp_val = exp(-((x - 1)*(x - 1)) / 2); } var_u3 = erand48(Nvar3_seed); if( var_u3 > 0.5 ) return(mean + (std * x)); else return(mean - (std * x)); } double LogNormal(float mean, float std) { double lognormal_x, tmp_var; lognormal_x = Normal(0,1); tmp_var = mean + (std * lognormal_x); return(exp(tmp_var)); } /***********************************************************************/ /* MAIN PROGRAM */ /***********************************************************************/ int main() { int i, j, sessions; int numconv, numconn, totconv, totconn; int sent, recd; int sessionid, connid, convid, portid; float now, nextarrival, lastarrival, lastconnfinish;; char dest; /* Seed the PRNG */ /* srandom(12345); */ srandom(time(NULL)); srandom(1234567); /* Initialization */ now = 0.0; sessions = 0; totconv = 0; totconn = 0; nextarrival = Exponential(MEAN_SESSION_IAT); while( sessions < NUM_SESSIONS ) { now = nextarrival; sessionid = sessions; portid = MIN_PORT + Uniform01()*(MAX_PORT - MIN_PORT); lastarrival = now; lastconnfinish = now; #ifdef DEBUG printf("Session %d starts at time %8.6f on port %d\n", sessionid, now, portid); #endif /* determine number of conversations */ numconv = Geometric(MEAN_CONV); totconv += numconv; #ifdef DEBUG printf("Session %d will have %d conversations\n", sessions, numconv); #endif for( convid = 0; convid < numconv; convid++ ) { numconn = Geometric(MEAN_CONN); totconn += numconn; if( convid > 0 ) now = lastconnfinish + Uniform01() * CONV_GAP; /* separate convs by a gap time */ else now = lastconnfinish; #ifdef DEBUG printf(" Conv %d of session %d will have %d connections starting %8.6f\n", convid, sessionid, numconn, now); #endif for( connid = 0; connid < numconn; connid++ ) { float rand, duration; /* generate destination for TCP connection */ rand = Uniform01(); if( rand < WEB_PROB ) dest = 'w'; else if( rand < WEB_PROB+MEDIA_PROB ) dest = 'm'; else dest = 'b'; /* generate bytes sent and received on TCP connection */ rand = Uniform01(); if( rand < TAIL_PROB ) { /* tail of distribution */ sent = LogNormal(SENT_BYTES_LOG_MEAN,SENT_BYTES_LOG_STD); while( (sent < 0) || (sent < 1024) ) /* safety check (or a hack!) */ { sent = LogNormal(SENT_BYTES_LOG_MEAN,SENT_BYTES_LOG_STD); } } else { /* main body of distribution below 64KB */ sent = pow(2.0, 8.0+Uniform01()*8.0); } rand = Uniform01(); if( rand < TAIL_PROB ) { if( dest == 'm' ) recd = LogNormal(RECD_BYTES_LOG_MEAN+1.0,RECD_BYTES_LOG_STD); else recd = LogNormal(RECD_BYTES_LOG_MEAN,RECD_BYTES_LOG_STD); while( (recd < 0) || (recd < 1024) ) /* safety check (or a hack!) */ { recd = LogNormal(RECD_BYTES_LOG_MEAN,RECD_BYTES_LOG_STD); } } else { /* main body of distribution below 64KB */ recd = pow(2.0, 8.0+Uniform01()*8.0); } #ifdef TRUNCATE /* hack: avoid extremely large values in tail of distribution */ if( sent > 50*1024*1024) /* 50 MB */ sent /= 2; if( recd > 100*1024*1024) /* 100 MB */ recd /= 2; #endif /* generate duration for TCP connection */ /* start with minimal possible duration based on data volume */ duration = (8.0 * sent) / UPLOAD_BANDWIDTH; duration += (8.0 * recd) / DOWNLOAD_BANDWIDTH; /* add some noise for TCP connection handshaking */ duration += 2.0*Uniform01(); /* add some time for persistent connection timeout */ if( Uniform01() < PERSISTENT_PROB ) duration += CONN_TIMEOUT; #ifdef DEBUG printf(" Conn %d conv %d session %d has dest %c sent %d recd %d dur %5.3f\n", connid, convid, sessionid, dest, sent, recd, duration); #endif /* print it out for the log file */ if( dest == 'b' ) { float randsplit; /* randomly split the data volume between sent and recd */ /* but with a definite bias to asymmetry (recd > sent) */ randsplit = Uniform01(); while( randsplit < 0.1 ) randsplit = Uniform01(); if( randsplit > 0.5 ) randsplit = 1.0 - randsplit; printf("%8.6f s %d v %d c %d p %d %c %d %d %5.3f %8.6f\n", now, sessionid, convid, connid, portid, dest, sent, recd, duration, now+duration); printf("%8.6f s %d v %d c %d p %d w* %1.0f %1.0f %5.3f %8.6f\n", now, sessionid, convid, connid, portid, sent*randsplit, recd*randsplit, duration, now+duration); portid++; printf("%8.6f s %d v %d c %d p %d m* %1.0f %1.0f %5.3f %8.6f\n", now, sessionid, convid, connid, portid, sent*(1.0-randsplit), recd*(1.0-randsplit), duration, now+duration); } else printf("%8.6f s %d v %d c %d p %d %c %d %d %5.3f %8.6f\n", now, sessionid, convid, connid, portid, dest, sent, recd, duration, now+duration); /* keep track of when the last connection of this conv/session ends */ if( now + duration > lastconnfinish ) { lastconnfinish = now + duration; #ifdef DEBUG2 printf("UPDATED lastconnfinish to %8.6f\n", lastconnfinish); #endif } /* update port number if needed for ephemeral ports */ portid++; if( portid > MAX_PORT ) portid = MIN_PORT; /* shift start time for next connection a bit randomly */ now += Uniform01() * duration; } } #ifdef DEBUG printf("Session %d ends at time %8.6f duration %8.6f\n", sessionid, lastconnfinish, lastconnfinish - lastarrival); #endif /* schedule next arrival, if any */ sessions++; if( sessions < NUM_SESSIONS ) { #ifdef FIXED_ARRIVAL_RATE nextarrival = lastarrival + Exponential(MEAN_SESSION_IAT); #else /* try to do time-varying Poisson arrivals */ int nowish; nowish = (int) nextarrival; if( nowish%ONE_DAY < 28800 ) /* 8:00am */ nextarrival = lastarrival + Exponential(10*MEAN_SESSION_IAT); else if( nowish%ONE_DAY > 57600 ) /* 4:00pm */ nextarrival = lastarrival + Exponential(5*MEAN_SESSION_IAT); else nextarrival = lastarrival + Exponential(MEAN_SESSION_IAT); #endif } else nextarrival = END_OF_TIME; } printf("\n"); printf("Tumblr Traffic Model:\n"); printf("Number of sessions: %d\n", NUM_SESSIONS); printf("Time: %8.6f Convs: %d Conns: %d\n", now, totconv, totconn); }