/* * Copyright (c) 2003 Parallelldatorcentrum * (Center for Parallel Computers, KTH, Sweden) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the Institute nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Header: /afs/pdc.kth.se/home/p/pek/src/c/sob.c,v 1.7 2004/02/21 17:33:15 pek Exp $ * * pek@pdc.kth.se $Date: 2004/02/21 17:33:15 $ * * Son of boom * * Test and time filesystem operations. * */ #define _FILE_OFFSET_BITS 64 #include #include #include #include #include #include #include #include #include #include #define K 1024 #define M 1048576 #define G 1073741824 #define STRMAX 64 /* State flags (expand MLEN when adding) */ #define MREAD 1 #define MWRITE 1<<1 #define MRAND 1<<2 #define MTERSE 1<<3 #define MVERIFY 1<<4 #define MLEN 5 #ifdef __sgi__ #define OFLAGS O_DIRECT #else #define OFLAGS 0 #endif #ifdef __osf__ #define atoll atol #endif #ifdef __GNUC__ #define uintmax_t unsigned long long #endif extern int errno; typedef struct { float dts[MLEN]; } dt_t; char version[] = "$Revision: 1.7 $\n"; char usage[] = "Usage : [-hrtvw] [ -b bs ] [ -R iter ] [ -n nfiles ] [ -o fanout ] [ -s size ]\n"; char *usage_long[] = { " Options :", " -b bs Block size", " -h This listing", " -R iter Randomly read iter files", " -n nfiles Number of files to read/write", " -o fanout Number of files per directory", " -r Read files", " -s size File size", " -t Terse, output \":\" separated fields", " -v Program version", " -V Verify files", " -w Write files" }; uintmax_t bs = 64*K; char *bufp; int terse_f = 0; /* Parse a number with [kmg] postscript, return value * is divided by div. */ uintmax_t postfix_parse(char *str, unsigned int div) { int l = strlen(str); if (isalpha(str[l - 1])) { str[l - 1] = (char)tolower((int)str[l - 1]); switch (str[l - 1]) { case 'k' : str[l - 1] = (char)NULL; return atoll(str) * (K / div); case 'm' : str[l - 1] = (char)NULL; return atoll(str) * (M / div); case 'g' : str[l - 1] = (char)NULL; return atoll(str) * (G / div); default : fprintf(stderr, "Error parsing string %s, invalid postfix.\n", str); exit(EXIT_FAILURE); } } return (atoll(str) / div); } /* Get time in seconds */ float gettime() { static struct timeval tv; static time_t secbase = 0; if (gettimeofday(&tv, NULL)) perror("Failed gettimeofday()"); if (secbase == 0) secbase = tv.tv_sec; return (float)(tv.tv_sec - secbase) + (tv.tv_usec / 1000000.0); } /* Fill with random data */ void rfill(void *p, size_t len) { int i; srand(0xf00); for (i = 0; i < len; i++) { ((char *)p)[i] = (unsigned char)(rand() % UCHAR_MAX); } } float write_files(char *fname, uintmax_t start_index, uintmax_t stop_index, uintmax_t fsize) { char str[STRMAX]; uintmax_t i, n; float t, dt; int fd; rfill((void *)bufp,bs); t = gettime(); for (n = start_index; n <= stop_index; n++) { snprintf(str, sizeof str, "%s.%u", fname, (unsigned long)n); if ((fd = open(str, OFLAGS|O_CREAT|O_TRUNC|O_RDWR, 0600)) == -1) { fprintf(stderr, "Failed to create file %s", str); perror(" "); exit(EXIT_FAILURE); } for (i = 0; i < fsize; i += bs) if (write(fd, bufp, bs) != bs) { perror("Complete write() failed"); exit(EXIT_FAILURE); } fsync(fd); close(fd); } dt = gettime() - t; return dt; } float read_files(char *fname, uintmax_t start_index, uintmax_t stop_index, uintmax_t fsize) { char str[STRMAX]; uintmax_t i, n; float t, dt; int fd; t = gettime(); for (n = start_index; n <= stop_index; n++) { snprintf(str, sizeof str, "%s.%u", fname, (unsigned long)n); if ((fd = open(str, OFLAGS|O_RDWR, 0600)) == -1) { fprintf(stderr, "Failed to open file %s", str); perror(" "); exit(EXIT_FAILURE); } for (i = 0; i < fsize; i += bs) if (read(fd, bufp, bs) != bs) { perror("Complete read() failed"); exit(EXIT_FAILURE); } fsync(fd); close(fd); } dt = gettime() - t; return dt; } float verify_files(char *fname, uintmax_t start_index, uintmax_t stop_index, uintmax_t fsize) { char *cbufp, str[STRMAX]; uintmax_t i, n; float t, dt; int fd; if ( (cbufp = (char *)malloc(bs)) == NULL) { perror("Failed to allocate buffer memory"); exit(EXIT_FAILURE); } rfill((void *)cbufp,bs); t = gettime(); for (n = start_index; n <= stop_index; n++) { snprintf(str, sizeof str, "%s.%u", fname, (unsigned long)n); if ((fd = open(str, OFLAGS|O_RDWR, 0600)) == -1) { fprintf(stderr, "Failed to open file %s", str); perror(" "); exit(EXIT_FAILURE); } for (i = 0; i < fsize; i += bs) { if (read(fd, bufp, bs) != bs) { perror("Complete read() failed"); exit(EXIT_FAILURE); } if (memcmp(bufp, cbufp, bs) != 0) { fprintf(stderr, "Failed file verification (%s).\n", str); exit(EXIT_FAILURE); } } close(fd); } dt = gettime() - t; free(cbufp); return dt; } void init_path(char *path, size_t len, char *dname) { if (dname[0] == '/') /* Absolute path */ strncpy(path, dname, len); else { /* Relative path */ if (dname[0] == '.' && dname[1] == '/') { dname++; len--; } if (getcwd(path, len) == NULL) { perror("getcwd() failed"); exit(EXIT_FAILURE); } if (strlen(path) + strlen(dname) >= len) { fprintf(stderr, "Path length exceeded.\n"); exit(EXIT_FAILURE); } printf("cwd is %s\n", path); strncpy(path + strlen(path), dname, len - strlen(path)); path[len - 1] = '\0'; } printf("path is %s\n", path); } void goto_dir(char *basepath, uintmax_t dirno) { char str[MAXPATHLEN]; if (snprintf(str, sizeof str, "%s.%u", basepath, (unsigned long)dirno) < 0) { fprintf(stderr, "Path exceeded MAXPATHLEN\n"); exit(EXIT_FAILURE); } if (mkdir(str, 0777) < 0 && errno != EEXIST) { perror(str); exit(EXIT_FAILURE); } if (chdir(str) < 0) { perror("Failed chdir():"); exit(EXIT_FAILURE); } } /* Return the total time taken. Put specific timings in dtp. */ float rand_read(char *path, char *fname, uintmax_t nfiles, uintmax_t fanout, uintmax_t fsize, uintmax_t iter) { uintmax_t fno, dno; float t, dt; t = gettime(); while (iter--) { fno = rand() % nfiles; if (fanout) { dno = fno / fanout; goto_dir(path, dno); } read_files(fname, fno, fno, fsize); } dt = gettime() - t; return dt; } /* Return the total time taken. Put specific timings in dtp. */ float mfile(dt_t *dtp, char *dname, char *fname, uintmax_t nfiles, uintmax_t fanout, uintmax_t fsize, uintmax_t iter, int mode) { char path[MAXPATHLEN]; uintmax_t dc, offset; float t, dt; init_path(path, sizeof path, dname); t = gettime(); if (mode & MWRITE) { for (dc = 0; dc * fanout < nfiles; dc++) { goto_dir(path, dc); offset = dc * fanout; dtp->dts[ffs(MWRITE)] += write_files(fname, offset, offset + fanout - 1, fsize); } } /* If this is done in the same loop as the writes it might * skew the read results due to caching */ if (mode & MREAD || mode & MVERIFY) { for (dc = 0; dc * fanout < nfiles; dc++) { goto_dir(path, dc); offset = dc * fanout; if (mode & MVERIFY) { dtp->dts[ffs(MVERIFY)] += verify_files(fname, offset, offset + fanout - 1, fsize); } if (mode & MREAD) { dtp->dts[ffs(MREAD)] += read_files(fname, offset, offset + fanout - 1, fsize); } } } if (mode & MRAND) { dtp->dts[ffs(MRAND)] = rand_read(path, fname, nfiles, fanout, fsize, iter); } dt = gettime() - t; return dt; } void print_result(dt_t *dtp, uintmax_t nfiles, uintmax_t fsize, uintmax_t iter, int mode) { #define MBs(i) (((fsize / dtp->dts[i]) / M) * nfiles) #define MBis(i) (((fsize / dtp->dts[i]) / M) * iter) #define MB (((float)fsize * nfiles) / M) #define MBi (((float)fsize * iter) / M) if (terse_f) { if (mode & MWRITE) printf("WRITE, %.3f, %.3f, %.1f, %lu, %lu\n", dtp->dts[ffs(MWRITE)], MBs(ffs(MWRITE)), MB, (unsigned long)nfiles, (unsigned long)bs/K); if (mode & MREAD) /* seconds, MB/s, MB, nfiles, kB */ printf("READ, %.3f, %.3f, %.1f, %lu, %lu\n", dtp->dts[ffs(MREAD)], MBs(ffs(MREAD)), MB, (unsigned long)nfiles, (unsigned long)bs/K); if (mode & MVERIFY) printf("VERIFY, %.3f, %.3f, %.1f, %lu, %lu\n", dtp->dts[ffs(MVERIFY)], MBs(ffs(MVERIFY)), MB, (unsigned long)nfiles, (unsigned long)bs/K); if (mode & MRAND) printf("RAND, %.3f, %.3f, %.1f, %lu, %lu\n", dtp->dts[ffs(MRAND)], MBis(ffs(MRAND)), MBi, (unsigned long)iter, (unsigned long)bs/K); } else { if (mode & MWRITE) printf("Wrote %.3f MB in %.3f s for %.3f MB/s, %lu files\n", MB, dtp->dts[ffs(MWRITE)], MBs(ffs(MWRITE)), (unsigned long)nfiles); if (mode & MREAD) printf("Read %.3f MB in %.3f s for %.3f MB/s, %lu files\n", MB, dtp->dts[ffs(MREAD)], MBs(ffs(MREAD)), (unsigned long)nfiles); if (mode & MVERIFY) printf("Verified %.3f MB in %.3f s for %.3f MB/s, %lu files\n", MB, dtp->dts[ffs(MVERIFY)], MBs(ffs(MVERIFY)), (unsigned long)nfiles); if (mode & MRAND) printf("Randomly read %lu files of total size %.3f MB in %.3f s for %.3f MB/s, %.3f files/s\n", (unsigned long)iter, MBi, dtp->dts[ffs(MRAND)], MBis(ffs(MRAND)), iter/(dtp->dts[ffs(MRAND)])); } #undef MBs #undef MB } void print_prelude(uintmax_t nfiles, uintmax_t fsize, uintmax_t iter, int mode) { if (!terse_f) { if (mode & MREAD) printf("Reading %lu files of size %.3fMB, blocksize %lukB\n", (unsigned long)nfiles, (((float)fsize)/M), (unsigned long)(bs/K)); else if (mode & MWRITE) printf("Writing %lu files of size %.3fMB, blocksize %lukB\n", (unsigned long)nfiles, (((float)fsize)/M), (unsigned long)(bs/K)); else if (mode & MVERIFY) printf("Verifying %lu files of size %.3fMB, blocksize %lukB\n", (unsigned long)nfiles, (((float)fsize)/M), (unsigned long)(bs/K)); else if (mode & MVERIFY) printf("Randomly reading %lu files of size %.3fMB, blocksize %lukB\n", (unsigned long)iter, (((float)fsize)/M), (unsigned long)(bs/K)); } } int main (int argc, char **argv) { extern char *optarg; char fname[STRMAX] = "testfile", path[MAXPATHLEN] = "./dir"; uintmax_t fsize = 64*M, nfiles = 1, fanout = 0, iter = 0; int mode = 0, c, i; dt_t dts; if (argc == 1) { puts(usage); exit(EXIT_SUCCESS); } while ( (c = getopt(argc, argv, "b:hn:o:rR:s:tvVw")) != -1) { switch (c) { case 'b' : bs = postfix_parse(optarg, 1); break; case 'n' : nfiles = postfix_parse(optarg, 1); break; case 'o' : fanout = postfix_parse(optarg, 1); break; case 'r' : mode |= MREAD; break; case 'R' : mode |= MRAND; iter = postfix_parse(optarg, 1); break; case 's' : fsize = postfix_parse(optarg, 1); break; case 't' : terse_f = 1; break; case 'v' : puts(version); break; case 'V' : mode |= MVERIFY; break; case 'w' : mode |= MWRITE; break; case 'h' : for (i = 0; i < (sizeof usage_long / sizeof usage_long[0]); i++) { puts(usage_long[i]); } exit(EXIT_SUCCESS); default : puts(usage); exit(EXIT_SUCCESS); } } if (optind < argc) { strncpy(fname, argv[optind], sizeof fname); fname[sizeof fname - 1] = '\0'; } if ((iter && ! (mode & MRAND)) || ( !iter && (mode & MRAND))) { for (i = 0; i < (sizeof usage_long / sizeof usage_long[0]); i++) { puts(usage_long[i]); exit(EXIT_FAILURE); } } memset(&dts, 0, sizeof dts); sync(); if ( (bufp = (char *)malloc(bs)) == NULL) { perror("Failed to allocate buffer memory"); exit(EXIT_FAILURE); } if (fanout) { print_prelude(nfiles, fsize, iter, mode); mfile(&dts, path, fname, nfiles, fanout, fsize, iter, mode); print_result(&dts, nfiles, fsize, iter, mode); } else { if (mode & MWRITE) { print_prelude(nfiles, fsize, iter, MWRITE); dts.dts[ffs(MWRITE)] = write_files(fname, 0, nfiles - 1, fsize); print_result(&dts, nfiles, fsize, iter, MWRITE); } if (mode & MRAND) { print_prelude(iter, fsize, iter, MRAND); dts.dts[ffs(MRAND)] = rand_read(path, fname, nfiles - 1, fanout, fsize, iter); print_result(&dts, nfiles, fsize, iter, MRAND); } if (mode & MVERIFY) { print_prelude(nfiles, fsize, iter, MVERIFY); dts.dts[ffs(MVERIFY)] = verify_files(fname, 0, nfiles - 1, fsize); print_result(&dts, nfiles, fsize, iter, MVERIFY); } else if (mode & MREAD) { print_prelude(nfiles, fsize, iter, MREAD); dts.dts[ffs(MREAD)] = read_files(fname, 0, nfiles - 1, fsize); print_result(&dts, nfiles, fsize, iter, MREAD); } } if (! (mode & (MREAD | MWRITE | MVERIFY | MRAND))) printf("No operation (-r, -w, -V) specified.\n"); return (EXIT_SUCCESS); }