/*****************************************************************************/ /* "NetPIPE" -- Network Protocol Independent Performance Evaluator. */ /* Copyright 1997, 1998 Iowa State University Research Foundation, Inc. */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation. You should have received a copy of the */ /* GNU General Public License along with this program; if not, write to the */ /* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* */ /* Files needed for use: */ /* * netpipe.c ---- Driver source */ /* * netpipe.h ---- General include file */ /* * TCP.c ---- TCP calls source */ /* * TCP.h ---- Include file for TCP calls and data structs */ /* * MPI.c ---- MPI calls source */ /* * MPI.h ---- Include file for MPI calls and data structs */ /* * PVM.c ---- PVM calls source */ /* * PVM.h ---- Include file for PVM calls and data structs */ /* 2002/03/18 --- Modified to specify server interfaces - Robbie Williamson */ /* (robbiew@us.ibm.com) */ /*****************************************************************************/ #include "netpipe.h" extern char *optarg; /* Return the current time in seconds, using a double precision number. */ double When() { struct timeval tp; gettimeofday(&tp, NULL); return ((double)tp.tv_sec + (double)tp.tv_usec * 1e-6); } int PrintUsage() { printf("\n NETPIPE USAGE \n\n"); printf("A: specify buffers alignment e.g.: <-A 1024>\n"); printf("a: asynchronous receive (a.k.a. preposted receive)\n"); #if defined(TCP) printf("b: specify send and receive buffer sizes e.g. <-b 32768>\n"); printf("h: specify hostname <-h host>\n"); printf("H: specify server hostname <-H HOST> e.g. Multiple NICs\n"); #endif printf("i: specify increment step size e.g. <-i 64>\n"); printf("l: lower bound start value e.g. <-i 1>\n"); printf("O: specify buffer offset e.g. <-O 127>\n"); printf("o: specify output filename <-o fn>\n"); printf("P: print on screen\n"); #if defined(TCP) printf("p: specify port e.g. <-p 5150>\n"); #endif printf("r: receiver\n"); printf("s: stream option\n"); printf("t: transmitter\n"); printf("u: upper bound stop value e.g. <-u 1048576>\n"); printf("\n"); exit(-12); } int main(int argc, char *argv[]) { FILE *out; /* Output data file */ char s[255]; /* Generic string */ char *memtmp; char *memtmp1; int c, /* option index */ i, j, n, nq, /* Loop indices */ asyncReceive = 0, /* Pre-post a receive buffer? */ bufoffset = 0, /* Align buffer to this */ bufalign = 16 * 1024, /* Boundary to align buffer to */ errFlag, /* Error occurred in inner testing loop */ nrepeat, /* Number of time to do the transmission */ len, /* Number of bytes to be transmitted */ inc = 0, /* Increment value */ trans = -1, /* Transmitter flag. 1 if transmitting. */ server = 0, /* Server flag. 1 if specifying server. */ detailflag = 0, /* Set to examine the signature curve detail */ bufszflag = 0, /* Set to change the TCP socket buffer size */ pert, /* Perturbation value */ start = 1, /* Starting value for signature curve */ end = MAXINT, /* Ending value for signature curve */ streamopt = 0, /* Streaming mode flag */ printopt = 0; /* Debug print statements flag */ ArgStruct args; /* Argumentsfor all the calls */ double t, t0, t1, t2, /* Time variables */ tlast, /* Time for the last transmission */ latency; /* Network message latency */ Data bwdata[NSAMP]; /* Bandwidth curve data */ short port = DEFPORT; /* Port number for connection */ #ifdef HAVE_GETRUSAGE struct rusage prev_rusage, curr_rusage; /* Resource usage */ double user_time, sys_time; /* User & system time used */ double best_user_time, best_sys_time; /* Total user & system time used */ double ut1, ut2, st1, st2; /* User & system time ctrs for variance */ double ut_var, st_var; /* Variance in user & system time */ #endif #ifdef MPI MPI_Init(&argc, &argv); #endif strcpy(s, "NetPIPE.out"); #ifndef MPI if (argc < 2) PrintUsage(); #endif /* Parse the arguments. See Usage for description */ while ((c = getopt(argc, argv, "PstrhH:p:o:A:O:l:u:i:b:a")) != -1) { switch (c) { case 'o': strcpy(s, optarg); break; case 't': trans = 1; break; case 'r': trans = 0; break; case 's': streamopt = 1; break; case 'l': /*detailflag = 1; */ start = atoi(optarg); if (start < 1) { fprintf(stderr, "Need a starting value >= 1\n"); exit(743); } break; case 'u': /*detailflag = 1; */ end = atoi(optarg); break; case 'i': detailflag = 1; inc = atoi(optarg); break; case 'b': bufszflag = 1; #ifdef TCP args.prot.rcvbufsz = atoi(optarg); args.prot.sndbufsz = args.prot.rcvbufsz; #endif break; case 'P': printopt = 1; break; case 'A': bufalign = atoi(optarg); break; case 'O': bufoffset = atoi(optarg); break; case 'p': port = atoi(optarg); break; case 'h': if (trans == 1) { args.host = (char *)malloc(strlen(optarg) + 1); strcpy(args.host, optarg); printf("host is %s\n", args.host); } else { fprintf(stderr, "Error: -t must be specified before -h\n"); exit(-11); } break; case 'H': if (trans == 0) { args.server_host = (char *)malloc(strlen(optarg) + 1); strcpy(args.server_host, optarg); printf("server is %s\n", args.server_host); server = 1; } else { fprintf(stderr, "Error: -r must be specified before -H\n"); exit(-11); } break; case 'a': asyncReceive = 1; break; default: PrintUsage(); exit(-12); } } if (start > end) { fprintf(stderr, "Start MUST be LESS than end\n"); exit(420132); } #if defined(TCP) || defined(PVM) /* It should be explicitly specified whether this is the transmitter or the receiver. */ if (trans < 0) { fprintf(stderr, "Error: either -t or -r must be specified\n"); exit(-11); } #endif args.nbuff = TRIALS; args.tr = trans; args.sr = server; args.port = port; #if defined(TCP) if (!bufszflag) { args.prot.sndbufsz = 0; args.prot.rcvbufsz = 0; } else fprintf(stderr, "Send and Recv Buffers are %d bytes\n", args.prot.sndbufsz); #endif Setup(&args); Establish(&args); if (args.tr) { if ((out = fopen(s, "w")) == NULL) { fprintf(stderr, "Can't open %s for output\n", s); exit(1); } } else out = stdout; args.bufflen = 1; args.buff = (char *)malloc(args.bufflen); args.buff1 = (char *)malloc(args.bufflen); if (asyncReceive) PrepareToReceive(&args); Sync(&args); t0 = When(); t0 = When(); t0 = When(); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &prev_rusage); #endif t0 = When(); for (i = 0; i < LATENCYREPS; i++) { if (args.tr) { SendData(&args); RecvData(&args); if (asyncReceive && (i < LATENCYREPS - 1)) { PrepareToReceive(&args); } } else { RecvData(&args); if (asyncReceive && (i < LATENCYREPS - 1)) { PrepareToReceive(&args); } SendData(&args); } } latency = (When() - t0) / (2 * LATENCYREPS); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &curr_rusage); #endif free(args.buff); free(args.buff1); if (args.tr) { SendTime(&args, &latency); } else { RecvTime(&args, &latency); } if (args.tr && printopt) { fprintf(stderr, "Latency: %.7f\n", latency); fprintf(stderr, "Now starting main loop\n"); } tlast = latency; if (inc == 0) { /* Set a starting value for the message size increment. */ inc = (start > 1) ? start / 2 : 1; } /* Main loop of benchmark */ for (nq = n = 0, len = start, errFlag = 0; n < NSAMP - 3 && tlast < STOPTM && len <= end && !errFlag; len = len + inc, nq++) { if (nq > 2 && !detailflag) { /* This has the effect of exponentially increasing the block size. If detailflag is false, then the block size is linearly increased (the increment is not adjusted). */ inc = ((nq % 2)) ? inc + inc : inc; } /* This is a perturbation loop to test nearby values */ for (pert = (!detailflag && inc > PERT + 1) ? -PERT : 0; pert <= PERT; n++, pert += (!detailflag && inc > PERT + 1) ? PERT : PERT + 1) { /* Calculate how many times to repeat the experiment. */ if (args.tr) { nrepeat = MAX((RUNTM / ((double)args.bufflen / (args.bufflen - inc + 1.0) * tlast)), TRIALS); SendRepeat(&args, nrepeat); } else { RecvRepeat(&args, &nrepeat); } /* Allocate the buffer */ args.bufflen = len + pert; if ((args.buff = (char *)malloc(args.bufflen + bufalign)) == (char *)NULL) { fprintf(stderr, "Couldn't allocate memory\n"); errFlag = -1; break; } if ((args.buff1 = (char *)malloc(args.bufflen + bufalign)) == (char *)NULL) { fprintf(stderr, "Couldn't allocate memory\n"); errFlag = -1; break; } /* Possibly align the data buffer: make memtmp and memtmp1 point to the original blocks (so they can be freed later), then adjust args.buff and args.buff1 if the user requested it. */ memtmp = args.buff; memtmp1 = args.buff1; if (bufalign != 0) args.buff += (bufalign - ((intptr_t) args.buff % bufalign) + bufoffset) % bufalign; if (bufalign != 0) args.buff1 += (bufalign - ((intptr_t) args.buff1 % bufalign) + bufoffset) % bufalign; if (args.tr && printopt) fprintf(stderr, "%3d: %9d bytes %4d times --> ", n, args.bufflen, nrepeat); /* Finally, we get to transmit or receive and time */ if (args.tr) { /* This is the transmitter: send the block TRIALS times, and if we are not streaming, expect the receiver to return each block. */ bwdata[n].t = LONGTIME; t2 = t1 = 0; #ifdef HAVE_GETRUSAGE ut1 = ut2 = st1 = st2 = 0.0; best_user_time = best_sys_time = LONGTIME; #endif for (i = 0; i < TRIALS; i++) { Sync(&args); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &prev_rusage); #endif t0 = When(); for (j = 0; j < nrepeat; j++) { if (asyncReceive && !streamopt) { PrepareToReceive(&args); } SendData(&args); if (!streamopt) { RecvData(&args); } } t = (When() - t0) / ((1 + !streamopt) * nrepeat); #ifdef HAVE_GETRUSAGE getrusage(RUSAGE_SELF, &curr_rusage); user_time = ((curr_rusage.ru_utime.tv_sec - prev_rusage.ru_utime.tv_sec) + (double) (curr_rusage.ru_utime.tv_usec - prev_rusage.ru_utime.tv_usec) * 1.0E-6) / ((1 + !streamopt) * nrepeat); sys_time = ((curr_rusage.ru_stime.tv_sec - prev_rusage.ru_stime.tv_sec) + (double) (curr_rusage.ru_stime.tv_usec - prev_rusage.ru_stime.tv_usec) * 1.0E-6) / ((1 + !streamopt) * nrepeat); ut2 += user_time * user_time; st2 += sys_time * sys_time; ut1 += user_time; st1 += sys_time; if ((user_time + sys_time) < (best_user_time + best_sys_time)) { best_user_time = user_time; best_sys_time = sys_time; } #endif if (!streamopt) { t2 += t * t; t1 += t; bwdata[n].t = MIN(bwdata[n].t, t); } } if (!streamopt) SendTime(&args, &bwdata[n].t); else RecvTime(&args, &bwdata[n].t); if (!streamopt) bwdata[n].variance = t2 / TRIALS - t1 / TRIALS * t1 / TRIALS; #ifdef HAVE_GETRUSAGE ut_var = ut2 / TRIALS - (ut1 / TRIALS) * (ut1 / TRIALS); st_var = st2 / TRIALS - (st1 / TRIALS) * (st1 / TRIALS); #endif } else { /* This is the receiver: receive the block TRIALS times, and if we are not streaming, send the block back to the sender. */ bwdata[n].t = LONGTIME; t2 = t1 = 0; for (i = 0; i < TRIALS; i++) { if (asyncReceive) { PrepareToReceive(&args); } Sync(&args); t0 = When(); for (j = 0; j < nrepeat; j++) { RecvData(&args); if (asyncReceive && (j < nrepeat - 1)) { PrepareToReceive(&args); } if (!streamopt) SendData(&args); } t = (When() - t0) / ((1 + !streamopt) * nrepeat); if (streamopt) { t2 += t * t; t1 += t; bwdata[n].t = MIN(bwdata[n].t, t); } } if (streamopt) SendTime(&args, &bwdata[n].t); else RecvTime(&args, &bwdata[n].t); if (streamopt) bwdata[n].variance = t2 / TRIALS - t1 / TRIALS * t1 / TRIALS; } tlast = bwdata[n].t; bwdata[n].bits = args.bufflen * CHARSIZE; bwdata[n].bps = bwdata[n].bits / (bwdata[n].t * 1024 * 1024); bwdata[n].repeat = nrepeat; if (args.tr) { fprintf(out, "%.7f %.7f %d %d %.7f", bwdata[n].t, bwdata[n].bps, bwdata[n].bits, bwdata[n].bits / 8, bwdata[n].variance); #ifdef HAVE_GETRUSAGE fprintf(out, " %.7f %.7f %.7f %.7f", ut1 / (double)TRIALS, st1 / (double)TRIALS, ut_var, st_var); #endif fprintf(out, "\n"); } fflush(out); free(memtmp); free(memtmp1); if (args.tr && printopt) { fprintf(stderr, " %6.3f Mbps in %.7f sec", bwdata[n].bps, tlast); #ifdef HAVE_GETRUSAGE fprintf(stderr, ", avg utime=%.7f avg stime=%.7f, ", ut1 / (double)TRIALS, st1 / (double)TRIALS); fprintf(stderr, "min utime=%.7f stime=%.7f, ", best_user_time, best_sys_time); fprintf(stderr, "utime var=%.7f stime var=%.7f", ut_var, st_var); #endif fprintf(stderr, "\n"); } } /* End of perturbation loop */ } /* End of main loop */ if (args.tr) fclose(out); CleanUp(&args); return (0); }