From: Larry McVoy (lm@slovax.Eng.Sun.COM)
Date: 05/06/93


From: lm@slovax.Eng.Sun.COM (Larry McVoy)
Subject: Re: hard disk performace survey, PLEASE READ / RUN / REPLY
Date: 7 May 1993 01:45:33 GMT

mef@hobbes.cs.washington.edu (Marc Fiuczynski) writes:
: Try using iozone which has been compiled and used on various platforms
: to test filesystem performance. I'm not sure if using DD will provide
: you with accurate raw disk i/o performance.

I'm not sure if it measures disks or caches. Here's a dd that does
the right thing, I'll vouch for it. I was paid to do file system work
for a year at SunSoft and doubled UFS performance. This tool was used
to measure that performance. Bug fixes to me, please.

#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of shell archive."
# Contents: lmdd.txt lmdd.1 lmdd.c
# Wrapped by lm@slovax on Tue Apr 13 22:31:48 1993
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'lmdd.txt' -a "${1}" != "-c" ; then
  echo shar: Will not clobber existing file \"'lmdd.txt'\"
else
echo shar: Extracting \"'lmdd.txt'\" \(4592 characters\)
sed "s/^X//" >'lmdd.txt' <<'END_OF_FILE'
X
X
X
XLMDD(1) LMDD(1)
X
X
XNAME
X lmdd - move io for performance and debugging tests
X
XSYNOPSIS
X lmdd [ option=value ] ...
X
XDESCRIPTION
X lmdd copies a specified input file to a specified output
X with possible conversions. This program is primarily use-
X ful for timing I/O since it prints out the timing statis-
X tics after completing.
X
XOPTIONS
X if=name Input file is taken from name; internal is
X the default. internal is a special file
X that acts like Sun's /dev/zero, i.e., it
X provides a buffer of zeros without doing a
X system call to get them.
X
X of=name Output file is taken from name; internal is
X the default. internal is a special file
X that acts like /dev/null, without doing a
X system call to get rid of the data.
X
X bs=n Input and output block size n bytes
X (default 8192). Note that this is differ-
X ent from dd(1), it has a 512 byte default.
X Also note that the block size can be fol-
X lowed by 'k' or 'm' to indicate kilo bytes
X (*1024) or megabytes (*1024*1024), respec-
X tively.
X
X ipat=n If n is non zero, expect a known pattern in
X the file (see opat). Mismatches will be
X displayed as "ERROR: off=%d want=%x
X got=%x". The pattern is a sequence of 4
X byte integers with the first 0, second 1,
X and so on. The default is not to check for
X the pattern.
X
X opat=n If n is non zero, generate a known pattern
X on the output stream. Used for debugging
X file system correctness. The default is
X not to generate the pattern.
X
X mismatch=n If n is non zero, stop at the first mis-
X matched value. Used with ipat.
X
X skip=n Skip n input blocks before starting copy.
X
X fsync=n If n is non-zero, call fsync(2) on the out-
X put file before exiting or printing timing
X statistics.
X
X
X
X
X 1
X
X
X
X
X
XLMDD(1) LMDD(1)
X
X
X sync=n If n is non-zero, call sync(2) before exit-
X ing or printing timing statistics.
X
X rand=n This argument, by default off, turns on
X random behavior. The argument is not a
X flag, it is a size, that size is used as
X the upper bound for the seeks. Also note
X that the block size can be followed by 'k'
X or 'm' to indicate kilo bytes (*1024) or
X megabytes (*1024*1024),
X
X flush=n If n is non-zero and mmap(2) is available,
X call msync(2) to invalidate the output
X file. This flushes the file to disk so
X that you don't have unmount/mount. It is
X not as good as mount/unmount because it
X just flushes file pages - it misses the
X indirect blocks which are still cached.
X Not supported on all systems, compile time
X option.
X
X rusage=n If n is non-zero, print rusage statistics
X as well as timing statistics. Not sup-
X ported on all systems, compile time option.
X
X count=n Copy only n input records.
X
XEXAMPLES
X This is the most common usage, the intent is to measure
X disk performance. The disk is a spare partition mounted
X on /spare.
X
X # mount /spare
X # lmdd if=internal of=/spare/XXX count=1000 fsync=1
X 7.81 MB in 3.78 seconds (2.0676 MB/sec)
X
X : Flush cache
X # umount /spare
X # mount /spare
X
X # lmdd if=/spare/XXX of=internal
X 7.81 MB in 2.83 seconds (2.7611 MB/sec)
X
X
XAUTHOR
X Larry McVoy, lm@sun.com
X Not copyrighted.
X
X
X
X
X
X
X
X
X
X
X 2
X
X
END_OF_FILE
if test 4592 -ne `wc -c <'lmdd.txt'`; then
    echo shar: \"'lmdd.txt'\" unpacked with wrong size!
fi
# end of 'lmdd.txt'
fi
if test -f 'lmdd.1' -a "${1}" != "-c" ; then
  echo shar: Will not clobber existing file \"'lmdd.1'\"
else
echo shar: Extracting \"'lmdd.1'\" \(3184 characters\)
sed "s/^X//" >'lmdd.1' <<'END_OF_FILE'
X.\" %W% %G%
X.TH LMDD 1
X.SH NAME
Xlmdd \- move io for performance and debugging tests
X.SH SYNOPSIS
X.B lmdd
X[
X.IB option = value
X] .\|.\|.
X.SH DESCRIPTION
X.B lmdd
Xcopies a specified input file to a specified output with possible
Xconversions. This program is primarily useful for timing I/O since it
Xprints out the timing statistics after completing.
X.SH OPTIONS
X.TP 15
X.BI if= name
XInput file is taken from
X.IR name ;
X.I internal
Xis the default.
X.I internal
Xis a special file that acts like Sun's
X.IR /dev/zero ,
Xi.e., it provides a buffer of zeros without doing a system call to get them.
X.TP
X.BI of= name
XOutput file is taken from
X.IR name ;
X.I internal
Xis the default.
X.I internal
Xis a special file that acts like
X.IR /dev/null ,
Xwithout doing a system call to get rid of the data.
X.TP
X.BI bs= n
XInput and output block size
X.I n
Xbytes (default 8192). Note that this is different from dd(1), it has
Xa 512 byte default. Also note that the block size can be followed
Xby 'k' or 'm' to indicate kilo bytes (*1024) or megabytes (*1024*1024),
Xrespectively.
X.TP
X.BI ipat= n
XIf
X.B n
Xis non zero, expect a known pattern in the file (see opat). Mismatches
Xwill be displayed as "ERROR: off=%d want=%x got=%x". The pattern is
Xa sequence of 4 byte integers with the first 0, second 1, and so on.
XThe default is not to check for the pattern.
X.TP
X.BI opat= n
XIf
X.B n
Xis non zero, generate a known pattern on the output stream. Used for
Xdebugging file system correctness.
XThe default is not to generate the pattern.
X.TP
X.BI mismatch= n
XIf
X.B n
Xis non zero, stop at the first mismatched value. Used with ipat.
X.TP
X.BI skip= n
XSkip
X.IR n ""
Xinput blocks before starting copy.
X.TP
X.BI fsync= n
XIf
X.I n
Xis non-zero, call fsync(2) on the output file before exiting or printing
Xtiming statistics.
X.TP
X.BI sync= n
XIf
X.I n
Xis non-zero, call sync(2) before exiting or printing
Xtiming statistics.
X.TP
X.BI rand= n
XThis argument, by default off, turns on random behavior. The argument is
Xnot a flag, it is a size, that size is used as the upper bound for the
Xseeks.
XAlso note that the block size can be followed
Xby 'k' or 'm' to indicate kilo bytes (*1024) or megabytes (*1024*1024),
X.TP
X.BI flush= n
XIf
X.I n
Xis non-zero and mmap(2) is available, call msync(2) to invalidate the
Xoutput file. This flushes the file to disk so that you don't have
Xunmount/mount. It is not as good as mount/unmount because it just
Xflushes file pages - it misses the indirect blocks which are still
Xcached. Not supported on all systems, compile time option.
X.TP
X.BI rusage= n
XIf
X.I n
Xis non-zero, print rusage statistics as well as timing statistics.
XNot supported on all systems, compile time option.
X.TP
X.BI count= n
XCopy only
X.IR n ""
Xinput records.
X.SH EXAMPLES
X.LP
XThis is the most common usage, the intent is to measure disk performance.
XThe disk is a spare partition mounted on /spare.
X.sp
X.nf
X.in +4
X# mount /spare
X# lmdd if=internal of=/spare/XXX count=1000 fsync=1
X7.81 MB in 3.78 seconds (2.0676 MB/sec)
X
X: Flush cache
X# umount /spare
X# mount /spare
X
X# lmdd if=/spare/XXX of=internal
X7.81 MB in 2.83 seconds (2.7611 MB/sec)
X.in
X.sp
X.fi
X.SH AUTHOR
XLarry McVoy, lm@sun.com
X.br
XNot copyrighted.
END_OF_FILE
if test 3184 -ne `wc -c <'lmdd.1'`; then
    echo shar: \"'lmdd.1'\" unpacked with wrong size!
fi
# end of 'lmdd.1'
fi
if test -f 'lmdd.c' -a "${1}" != "-c" ; then
  echo shar: Will not clobber existing file \"'lmdd.c'\"
else
echo shar: Extracting \"'lmdd.c'\" \(7406 characters\)
sed "s/^X//" >'lmdd.c' <<'END_OF_FILE'
Xstatic char sccsid[] = "@(#)lmdd.c 1.11";
X
X/*
X * lmdd, lm@sun.com (Larry McVoy)
X *
X * Uncopyrighted, do whatever you want with it.
X *
X * defaults:
X * bs=8k
X * count=forever
X * if=internal
X * of=internal
X * ipat=0
X * opat=0
X * mismatch=0
X * rusage=0
X * flush=0
X * rand=0
X * shorthands:
X * recognizes 'k' or 'm' at the end of a number for 1024 & 1024^2
X * recognizes "internal" as an internal /dev/zero /dev/null file.
X */
X
X/*
X * Compile time configs
X */
X#ifdef sun
X#define FLUSH
X#define RUSAGE
X#endif
X
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#ifdef RUSAGE
X#include <sys/resource.h>
X#endif
X#ifdef FLUSH
X#include <unistd.h>
X#include <sys/mman.h>
X#include <sys/stat.h>
X#endif
X#ifdef sun
X#include <machine/cpu.h> /* XXX - assumes 4.1.2 at least */
X#endif
X
X#define USECS_PER_SEC 1000000.0
X#define USECS_PER_TICK 1000
X
X#define uchar unsigned char
X
Xint out, Fsync, Sync, Flush, Rand, Bsize, ru, c;/* done needs it */
Xchar *cmds[] = {
X "if", /* input file */
X "of", /* output file */
X "ipat", /* check input for pattern */
X "opat", /* generate pattern on output */
X "mismatch", /* stop at first mismatch */
X "bs", /* block size */
X "count", /* number of blocks */
X "skip", /* skip this number of blocks on input */
X "fsync", /* fsync output before exit */
X "sync", /* sync output before exit */
X "rand", /* do randoms over the specified size */
X /* must be power of two, not checked */
X#ifdef RUSAGE
X "rusage", /* dump rusage stats */
X#endif
X#ifdef FLUSH
X "flush", /* map in out and invalidate (flush) */
X#endif
X 0,
X};
X
Xmain(ac, av)
X char **av;
X{
X uint *buf;
X int misses, mismatch, outpat, inpat, in, gotcnt, count;
X int skip;
X int size;
X extern char *valloc();
X void done();
X int i;
X
X for (i = 1; i < ac; ++i) {
X chkarg(av[i]);
X }
X signal(SIGINT, done);
X misses = mismatch = getarg("mismatch=", ac, av);
X inpat = getarg("ipat=", ac, av);
X outpat = getarg("opat=", ac, av);
X Bsize = getarg("bs=", ac, av);
X if (Bsize < 0)
X Bsize = 8192;
X Fsync = getarg("fsync=", ac, av);
X Sync = getarg("sync=", ac, av);
X Rand = getarg("rand=", ac, av);
X if (Rand != -1) {
X size = (Rand - 1) & ~511;
X }
X#ifdef RUSAGE
X ru = getarg("rusage=", ac, av);
X#endif
X count = getarg("count=", ac, av);
X#ifdef FLUSH
X Flush = getarg("flush=", ac, av);
X#endif
X if (count < 0)
X gotcnt = 0;
X else
X gotcnt = 1;
X c = 0;
X skip = getarg("skip=", ac, av);
X
X if (inpat != -1 || outpat != -1 && (Bsize & 3)) {
X printf("Block size must be word aligned\n");
X exit(1);
X }
X if (!(buf = (uint *) valloc((unsigned) Bsize))) {
X perror("valloc");
X exit(1);
X }
X bzero((char *) buf, Bsize);
X
X start();
X /*
X * We want this here because otherwise the accounting gets screwed up
X */
X in = getfile("if=", ac, av);
X out = getfile("of=", ac, av);
X if (skip > 0)
X lseek(in, skip * Bsize, 0);
X for (;;) {
X register x;
X
X if (gotcnt && count-- <= 0)
X done();
X /*
X * Set the seek pointer if doing randoms
X */
X if (Rand != -1) {
X u_int off = lrand48() & size;
X
X if (in != -1)
X lseek(in, off, 0);
X if (out != -1)
X lseek(out, off, 0);
X }
X if (in >= 0)
X x = read(in, buf, Bsize);
X else
X x = Bsize;
X if (x <= 0)
X done();
X if (inpat != -1) {
X register foo, cnt;
X
X for (foo = 0, cnt = x >> 2; cnt--; foo++) {
X if (buf[foo] != (uint) (c + foo)) {
X printf("ERROR: off=%d want=%x got=%x\n",
X c + foo, c + foo, buf[foo]);
X if (mismatch != -1 && --misses == 0)
X done();
X }
X }
X }
X if (outpat != -1) {
X register foo, cnt;
X
X for (foo = 0, cnt = x >> 2; cnt--; foo++)
X buf[foo] = c + foo;
X }
X if (out >= 0)
X if (write(out, buf, x) != x)
X done();
X c += x >> 2;
X }
X}
X
Xchkarg(arg)
X char *arg;
X{
X int i;
X char *a, *b;
X
X for (i = 0; cmds[i]; ++i) {
X for (a = arg, b = cmds[i]; *a && *b && *a == *b; a++, b++)
X ;
X if (*a == '=')
X return (0);
X }
X printf("Bad arg: %s\n", arg);
X exit(1);
X /*NOTREACHED*/
X}
X
Xvoid
Xdone()
X{
X close(1);
X open("/dev/tty", 1);
X if (Sync > 0)
X sync();
X if (Fsync > 0)
X fsync(out);
X#ifdef FLUSH
X if (Flush > 0)
X flush();
X#endif
X stop();
X#ifdef RUSAGE
X if (ru != -1)
X rusage();
X#endif
X ptime(c << 2);
X exit(0);
X}
X
X#define secs(tv) (tv.tv_sec + tv.tv_usec / USECS_PER_SEC)
X#define mine(f) (r.f - ru_start.f)
X
X#ifdef RUSAGE
Xstruct rusage ru_start;
X
Xrusage()
X{
X struct rusage r;
X double sys, user, idle;
X double per;
X double timespent();
X
X getrusage(RUSAGE_SELF, &r);
X sys = secs(r.ru_stime) - secs(ru_start.ru_stime);
X user = secs(r.ru_utime) - secs(ru_start.ru_utime);
X idle = timespent() - (sys + user);
X per = idle / timespent() * 100;
X printf("real=%.2f sys=%.2f user=%.2f idle=%.2f stall=%.0f%% ",
X timespent(), sys, user, idle, per);
X printf("rd=%d wr=%d min=%d maj=%d\n",
X mine(ru_inblock), mine(ru_oublock),
X mine(ru_minflt), mine(ru_majflt));
X}
X#endif
X
Xgetarg(s, ac, av)
X char *s;
X char **av;
X{
X register len, i;
X
X len = strlen(s);
X
X for (i = 1; i < ac; ++i)
X if (!strncmp(av[i], s, len)) {
X register bs = atoi(&av[i][len]);
X
X if (rindex(&av[i][len], 'k'))
X bs *= 1024;
X else if (rindex(&av[i][len], 'm'))
X bs *= (1024 * 1024);
X return (bs);
X }
X return (-1);
X}
X
Xchar *output;
X
Xgetfile(s, ac, av)
X char *s;
X char **av;
X{
X register ret, len, i;
X
X len = strlen(s);
X
X for (i = 1; i < ac; ++i) {
X if (!strncmp(av[i], s, len)) {
X if (av[i][0] == 'o') {
X if (!strcmp("of=internal", av[i]))
X return (-2);
X ret = creat(&av[i][len], 0644);
X if (ret == -1)
X error(&av[i][len]);
X output = &av[i][len];
X return (ret);
X } else {
X if (!strcmp("if=internal", av[i]))
X return (-2);
X ret = open(&av[i][len], 0);
X if (ret == -1)
X error(&av[i][len]);
X return (ret);
X }
X }
X }
X return (-2);
X}
X
X#ifdef FLUSH
Xflush()
X{
X int fd;
X struct stat sb;
X caddr_t where;
X
X if (output == NULL || (fd = open(output, 2)) == -1) {
X warning("No output file");
X return;
X }
X if (fstat(fd, &sb) == -1 || sb.st_size == 0) {
X warning(output);
X return;
X }
X where = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
X msync(where, sb.st_size, MS_INVALIDATE);
X /* XXX - didn't unmap */
X}
X#endif
X
X/*
X * utilities for timing
X */
X#include "stdio.h"
X#include "sys/types.h"
X#include "sys/time.h"
X
Xstatic struct timeval t1, t2;
X
Xwarning(s)
X char *s;
X{
X perror(s);
X return (-1);
X}
X
Xerror(s)
X char *s;
X{
X perror(s);
X exit(1);
X}
X
Xstart() {
X gettimeofday(&t1, (struct timezone *) 0);
X#ifdef RUSAGE
X getrusage(RUSAGE_SELF, &ru_start);
X#endif
X}
X
Xstop() {
X gettimeofday(&t2, (struct timezone *) 0);
X}
X
Xptime(bytes) {
X ptransfer(bytes, &t1, &t2);
X}
X
Xdouble
Xtimespent()
X{
X struct timeval td;
X
X tvsub(&td, &t2, &t1);
X return (td.tv_sec + td.tv_usec/USECS_PER_SEC);
X}
X
Xptransfer(bytes, t0, t1)
X struct timeval *t0, *t1;
X{
X struct timeval td;
X double s, bytespersec;
X
X tvsub(&td, t1, t0);
X s = td.tv_sec + td.tv_usec/USECS_PER_SEC;
X#define nz(x) ((x) == 0 ? 1 : (x))
X#define MB (1024*1024.0)
X bytespersec = bytes / nz(s);
X printf("%.2fMB in %.2f seconds (%.4f MB/s) %.2f IOP/s",
X bytes / MB, s, bytespersec / MB, (bytes / Bsize) / s);
X if (Bsize != 8192)
X printf(" bsize=%d", Bsize);
X if (Rand != -1)
X printf(" rand=%.2fMB", Rand / MB);
X printf("\n");
X}
X
Xtvsub(tdiff, t1, t0)
X struct timeval *tdiff, *t1, *t0;
X{
X
X tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
X if ( t0->tv_usec > t1->tv_usec ) {
X tdiff->tv_sec--;
X tdiff->tv_usec = (USECS_PER_SEC - t0->tv_usec) + t1->tv_usec;
X } else
X tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
X
X}
END_OF_FILE
if test 7406 -ne `wc -c <'lmdd.c'`; then
    echo shar: \"'lmdd.c'\" unpacked with wrong size!
fi
# end of 'lmdd.c'
fi
echo shar: End of shell archive.
exit 0