Adapted from a LKML posted program. I think I added write support, this for benchmarking random access writes on flash.
1
2 /* Simple multi-threaded I/O benchmark program.
3 * Copyright (C) Michael Tokarev, mjt@tls.msk.ru
4 * Public domain.
5 *
6 * To compile:
7 * gcc -o iot iot.c -lpthread
8 * To run:
9 * Either with disk device or with pre-existing file.
10 * ./iot [options] filename
11 * Filename is the file or device to test on.
12 * By default it uses 8Kb I/O blocks and does sequential read test
13 * until interrupted.
14 * To indicate when to stop:
15 * -t sec - run for this many seconds, say, 30, to eliminate random
16 * noise.
17 * -i num - perform this many I/O operations
18 * To indicate R/W mode:
19 * -wn, -Wn, -rn, -Rn --
20 * perform linear or random write (note: all data will be lost!),
21 * or linear or random read, using given number of threads (n).
22 * I/O modes:
23 * -s - syncronous write (O_SYNC)
24 * -d - direct I/O (O_DIRECT)
25 * -b bs - block size in bytes
26 * And finally:
27 * -h - display usage.
28 * Example:
29 * ./iot -t30 -W4 -R4 -d -b8192 /dev/sdb
30 * perform random read/write test (4 readers and 4 writers)
31 * for 30 seconds using direct I/O and block size of 8Kb.
32 *
33 * Note: for small blocksize (<64Kb at least) and using direct random I/O,
34 * nowadays drives sometimes gives transfer rates below 1Mb/sec - this is
35 * expectable, don't be afraid of so low numbers. The reason is simple:
36 * in order to access a given block of data, a disk drive has to seek to the
37 * right track (average seek time) and wait for the right sector to be near
38 * the head (rotation latency). Sum up the two, and divide 1 sec to the
39 * result -- you'll have max number of requests/sec a drive can perform,
40 * not counting the actual data transfer (which reduces this number further).
41 * With, say, 5ms seek time + rotation latency, we'll have 200 requests/sec,
42 * which, with 4Kb request size, will be about 800Kb/sec - which is below
43 * 1Mb/sec, not counting the actual transfer...
44 *
45 */
46
47 #define _GNU_SOURCE
48 #define _BSD_SOURCE
49 #define _LARGEFILE_SOURCE
50 #define _FILE_OFFSET_BITS 64
51
52 //#define USE_DEV_URANDOM /* was not a good idea */
53
54 #include <sys/types.h>
55 #include <unistd.h>
56 #include <fcntl.h>
57 #include <errno.h>
58 #include <stdlib.h>
59 #include <stdio.h>
60 #include <sys/time.h>
61 #include <sys/ioctl.h>
62 #include <signal.h>
63 #include <pthread.h>
64 #include <string.h>
65
66 #ifndef BLKGETSIZE64
67 #define BLKGETSIZE64 _IOR(0x12,114,size_t)
68 /* linux-specific. return device size in bytes (u64 *arg) */
69 #endif
70
71 static void edie(const char *what) {
72 fprintf(stderr, "%s: %m \n", what);
73 exit(1);
74 }
75
76 #ifdef USE_DEV_URANDOM
77 static int randfd;
78 #endif
79 static int oflags; // open flags
80 static char *fn; // filename
81 static unsigned bs = 8192; // block size
82 static unsigned bc; // block count (device size in blocks)
83 static unsigned bm; // blocks to do
84
85 #define MFrnd 1
86 #define MFwrt 2
87 #define LinRd 0
88 #define RndRd MFrnd
89 #define LinWr MFwrt
90 #define RndWr (MFrnd|MFwrt)
91
92 struct state {
93 int fd;
94 char *buf;
95 unsigned ioc; // I/O count
96 int (*workfn)(struct state *, unsigned blocknr);
97 unsigned (*posfn)(struct state *s);
98 unsigned opi; // operation index
99 unsigned i; // curidx
100 double stime; // start time
101 unsigned bn; // current block number for linear i/o
102 };
103 static unsigned int alternate;
104 static unsigned tioc; // total i/o count
105 static struct state *states;
106 static unsigned nt[4];
107 static unsigned ntt;
108 static volatile unsigned running;
109 static const char *const ion[4] = { "LinRd", "RndRd", "LinWr", "RndWr" };
110
111 static pthread_mutex_t rnmtx = PTHREAD_MUTEX_INITIALIZER;
112 static pthread_cond_t rncond = PTHREAD_COND_INITIALIZER;
113
114 static double curtime(void) {
115 struct timeval tv;
116 gettimeofday(&tv, NULL);
117 return tv.tv_sec + tv.tv_usec / 1000000.0;
118 }
119
120 static unsigned randpos(struct state *s) {
121 unsigned n;
122 #ifdef USE_DEV_URANDOM
123 read(randfd, &n, sizeof(n));
124 #else
125 n = lrand48();
126 #endif
127 s = s;
128 if(alternate == 1) {
129 if(n > bc/2) {
130 return bc-1;
131 } else {
132 return 0;
133 }
134 }
135 return n % bc;
136 }
137
138 static unsigned linpos(struct state *s) {
139 if (s->bn >= bc)
140 s->bn = 0;
141 return s->bn++;
142 }
143
144 static int wwriter(struct state *s, unsigned b) {
145 return pwrite(s->fd, s->buf, bs, (off_t)b * bs);
146 }
147 static int wreader(struct state *s, unsigned b) {
148 return pread(s->fd, s->buf, bs, (off_t)b * bs);
149 }
150
151 static void pst(FILE *f) {
152 double ct = curtime();
153 double r[4] = { 0, 0, 0, 0 };
154 unsigned c[4] = { 0, 0, 0, 0 };
155 unsigned i;
156 double d;
157 for(i = 0; i < ntt; ++i) {
158 d = ct - states[i].stime;
159 r[states[i].opi] += states[i].ioc / d;
160 c[states[i].opi] += states[i].ioc;
161 }
162 #if 1
163 for(i = 0; i < 4; ++i)
164 if (c[i])
165 fprintf(f, " %s %u %.2f", ion[i], c[i], r[i] * bs / 1024 / 1024);
166 #endif
167 }
168
169 static void incc() {
170 if (!(++tioc % 1000))
171 pthread_cond_signal(&rncond);
172 }
173 static void decnr() {
174 pthread_mutex_lock(&rnmtx);
175 --running;
176 pthread_mutex_unlock(&rnmtx);
177 pthread_cond_broadcast(&rncond);
178 }
179
180 static volatile int term;
181
182 void *worker(void *arg) {
183 struct state *s = arg;
184 s->workfn = s->opi & MFwrt ? wwriter : wreader;
185 s->posfn = s->opi & MFrnd ? randpos : linpos;
186 s->fd = open(fn, (s->opi & MFwrt ? O_WRONLY : O_RDONLY) | oflags);
187 if (s->fd < 0) {
188 int e = errno;
189 decnr();
190 errno = e;
191 edie(fn);
192 }
193 s->stime = curtime();
194 for(;;) {
195 if (term) break;
196 if (s->workfn(s, s->posfn(s)) < 0) {
197 perror(ion[s->opi]);
198 break;
199 }
200 ++s->ioc;
201 incc();
202 if (bm && s->ioc >= bm) break;
203 }
204 decnr();
205 return 0;
206 }
207
208 static void sig(int s) {
209 term = s;
210 }
211
212 int main(int argc, char **argv) {
213 int c;
214 unsigned i, j;
215 unsigned tm = 0;
216 struct state *s;
217 char *buf;
218 struct timeval first,
219 second,
220 lapsed;
221
222 while((c = getopt(argc, argv, "r::R::w::W::adsb:n:i:t:h")) != EOF) switch(c) {
223 case 'r': nt[LinRd] = optarg ? atoi(optarg) : 1; break;
224 case 'R': nt[RndRd] = optarg ? atoi(optarg) : 1; break;
225 case 'w': nt[LinWr] = optarg ? atoi(optarg) : 1; break;
226 case 'W': nt[RndWr] = optarg ? atoi(optarg) : 1; break;
227 case 'd': oflags |= O_DIRECT; break;
228 case 's': oflags |= O_SYNC; break;
229 case 'b': bs = atoi(optarg); break;
230 case 'n': bc = atoi(optarg); break;
231 case 'i': bm = atoi(optarg); break;
232 case 't': tm = atoi(optarg); break;
233 case 'a': alternate = 1; break;
234 case 'h':
235 puts(
236 "iotest: perform I/O speed test\n"
237 "Usage is: iotest [options] device-or-file\n"
238 "options:\n"
239 " -r[n] - linear read test (n readers)\n"
240 " -R[n] - random read test (n readers)\n"
241 " -w[n] - linear write test (n writers)\n"
242 " -W[n] - random write test (n writers)\n"
243 " -d - use direct I/O (O_DIRECT)\n"
244 " -s - use syncronous I/O (O_SYNC)\n"
245 " -b bs - blocksize (default is 8192)\n"
246 " -n bc - block count (default is whole device/file)\n"
247 " -i nb - number of I/O iterations to perform\n"
248 " -t sec - time to spend on all I/O\n"
249 " -h - this help\n"
250 "It's ok to specify all, one or some of -r,-R,-w and -W\n"
251 );
252 return 0;
253 default: fprintf(stderr, "try `iotest -h' for help\n"); exit(1);
254 }
255
256 if (optind + 1 != argc) {
257 fprintf(stderr, "exactly one device/file argument expected\n");
258 return 1;
259 }
260 fn = argv[optind];
261
262 ntt = nt[0] + nt[1] + nt[2] + nt[3];
263 if (!ntt)
264 nt[LinRd] = ntt = 1;
265
266 c = open(fn, (nt[LinWr] + nt[RndWr] ? O_RDWR : O_RDONLY) | oflags);
267 if (c < 0) edie(fn);
268 if (!bc) {
269 unsigned long long sz;
270 struct stat st;
271 fstat(c, &st);
272 if (st.st_size) sz = st.st_size;
273 else ioctl(c, BLKGETSIZE64, &sz);
274 bc = sz / bs;
275 fprintf(stderr, "size = %lld (%u blocks)\n", sz, bc);
276 }
277 close(c);
278 if (nt[RndRd] || nt[RndWr]) {
279 #ifdef USE_DEV_URANDOM
280 randfd = open("/dev/urandom", O_RDONLY);
281 if (randfd < 0) edie("/dev/urandom");
282 #else
283 #if 0
284 struct timeval tv;
285 gettimeofday(&tv, NULL);
286 srand48(tv.tv_usec ^ getpid());
287 #else
288 srand48(0xfeda432); // arbitrary, to get repeated values on repeated runs
289 #endif
290 #endif
291 }
292
293 gettimeofday(&first, NULL);
294
295 states = calloc(ntt, sizeof(*states));
296 s = states;
297
298 buf = valloc(ntt * bs);
299 if (tm) {
300 signal(SIGALRM, sig);
301 alarm(tm);
302 }
303 running = ntt;
304 for(j = 0; j < 4; ++j)
305 for(i = 0; i < nt[j]; ++i) {
306 pthread_t t;
307 s->buf = buf; buf += bs;
308 s->opi = j;
309 s->i = i;
310 pthread_create(&t, NULL, worker, s++);
311 }
312 while(running) {
313 pthread_cond_wait(&rncond, &rnmtx);
314 putc('\r', stderr);
315 pst(stderr);
316 }
317
318 putc('\r', stderr);
319 pst(stdout);
320 putc('\n', stdout);
321
322 gettimeofday(&second, NULL);
323
324 if (first.tv_usec > second.tv_usec) {
325 second.tv_usec += 1000000;
326 second.tv_sec--;
327 }
328
329 lapsed.tv_usec = second.tv_usec - first.tv_usec;
330 lapsed.tv_sec = second.tv_sec - first.tv_sec;
331
332
333
334 if (nt[RndRd] || nt[RndWr]) {
335 printf("I/O latency: %f\n", (lapsed.tv_sec*1000+(lapsed.tv_usec/1000))/(float)tioc);
336
337 }
338
339 return 0;
340 }
341