1 /*******************************************************************************
2 * The BYTE UNIX Benchmarks - Release 3
3 * Module: fstime.c SID: 3.5 5/15/91 19:30:19
4 *
5 *******************************************************************************
6 * Bug reports, patches, comments, suggestions should be sent to:
7 *
8 * Ben Smith, Rick Grehan or Tom Yager
9 * ben@bytepb.byte.com rick_g@bytepb.byte.com tyager@bytepb.byte.com
10 *
11 *******************************************************************************
12 * Modification Log:
13 * $Header: fstime.c,v 3.4 87/06/22 14:23:05 kjmcdonell Beta $
14 * 10/19/89 - rewrote timing calcs and added clock check (Ben Smith)
15 * 10/26/90 - simplify timing, change defaults (Tom Yager)
16 * 11/16/90 - added better error handling and changed output format (Ben Smith)
17 * 11/17/90 - changed the whole thing around (Ben Smith)
18 * 2/22/91 - change a few style elements and improved error handling (Ben Smith)
19 * 4/17/91 - incorporated suggestions from Seckin Unlu (seckin@sumac.intel.com)
20 * 4/17/91 - limited size of file, will rewind when reaches end of file
21 * 7/95 - fixed mishandling of read() and write() return codes
22 * Carl Emilio Prelz <fluido@telepac.pt>
23 * 12/95 - Massive changes. Made sleep time proportional increase with run
24 * time; added fsbuffer and fsdisk variants; added partial counting
25 * of partial reads/writes (was *full* credit); added dual syncs.
26 * David C Niemi <niemi@tux.org>
27 * 10/22/97 - code cleanup to remove ANSI C compiler warnings
28 * Andy Kahn <kahn@zk3.dec.com>
29 * 9/24/07 - Separate out the read and write tests;
30 * output the actual time used in the results.
31 * Ian Smith <johantheghost at yahoo period com>
32 ******************************************************************************/
33 char SCCSid[] = "@(#) @(#)fstime.c:3.5 -- 5/15/91 19:30:19";
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <unistd.h>
41 #include <sys/time.h>
42
43 #define SECONDS 10
44
45 #define MAX_BUFSIZE 8192
46
47 /* This must be set to the smallest BUFSIZE or 1024, whichever is smaller */
48 #define COUNTSIZE 256
49 #define HALFCOUNT (COUNTSIZE/2) /* Half of COUNTSIZE */
50
51 #define FNAME0 "dummy0"
52 #define FNAME1 "dummy1"
53
54 int w_test(int timeSecs);
55 int r_test(int timeSecs);
56 int c_test(int timeSecs);
57
58 long read_score = 1, write_score = 1, copy_score = 1;
59
60 /****************** GLOBALS ***************************/
61
62 /* The buffer size for the tests. */
63 int bufsize = 1024;
64
65 /*
66 * The max number of 1024-byte blocks in the file.
67 * Don't limit it much, so that memory buffering
68 * can be overcome.
69 */
70 int max_blocks = 2000;
71
72 /* The max number of BUFSIZE blocks in the file. */
73 int max_buffs = 2000;
74
75 /* Countable units per 1024 bytes */
76 int count_per_k;
77
78 /* Countable units per bufsize */
79 int count_per_buf;
80
81 /* The actual buffer. */
82 /* char *buf = 0; */
83 /* Let's carry on using a static buffer for this, like older versions
84 * of the code did. It turns out that if you use a malloc buffer,
85 * it goes 50% slower on reads, when using a 4k buffer -- at least on
86 * my OpenSUSE 10.2 system.
87 * What up wit dat?
88 */
89 char buf[MAX_BUFSIZE];
90
91 int f;
92 int g;
93 int i;
94 void stop_count();
95 void clean_up();
96 int sigalarm = 0;
97
98 /******************** MAIN ****************************/
99
main(argc,argv)100 int main(argc, argv)
101 int argc;
102 char *argv[];
103 {
104 /* The number of seconds to run for. */
105 int seconds = SECONDS;
106
107 /* The type of test to run. */
108 char test = 'c';
109
110 int status;
111 int i;
112
113 for (i = 1; i < argc; ++i) {
114 if (argv[i][0] == '-') {
115 switch (argv[i][1]) {
116 case 'c':
117 case 'r':
118 case 'w':
119 test = argv[i][1];
120 break;
121 case 'b':
122 bufsize = atoi(argv[++i]);
123 break;
124 case 'm':
125 max_blocks = atoi(argv[++i]);
126 break;
127 case 't':
128 seconds = atoi(argv[++i]);
129 break;
130 case 'd':
131 if (chdir(argv[++i]) < 0) {
132 perror("fstime: chdir");
133 exit(1);
134 }
135 break;
136 default:
137 fprintf(stderr, "Usage: fstime [-c|-r|-w] [-b <bufsize>] [-m <max_blocks>] [-t <seconds>]\n");
138 exit(2);
139 }
140 } else {
141 fprintf(stderr, "Usage: fstime [-c|-r|-w] [-b <bufsize>] [-m <max_blocks>] [-t <seconds>]\n");
142 exit(2);
143 }
144 }
145
146 if (bufsize < COUNTSIZE || bufsize > MAX_BUFSIZE) {
147 fprintf(stderr, "fstime: buffer size must be in range %d-%d\n",
148 COUNTSIZE, 1024*1024);
149 exit(3);
150 }
151 if (max_blocks < 1 || max_blocks > 1024*1024) {
152 fprintf(stderr, "fstime: max blocks must be in range %d-%d\n",
153 1, 1024*1024);
154 exit(3);
155 }
156 if (seconds < 1 || seconds > 3600) {
157 fprintf(stderr, "fstime: time must be in range %d-%d seconds\n",
158 1, 3600);
159 exit(3);
160 }
161
162 max_buffs = max_blocks * 1024 / bufsize;
163 count_per_k = 1024 / COUNTSIZE;
164 count_per_buf = bufsize / COUNTSIZE;
165
166 /*
167 if ((buf = malloc(bufsize)) == 0) {
168 fprintf(stderr, "fstime: failed to malloc %d bytes\n", bufsize);
169 exit(4);
170 }
171 */
172
173 if((f = creat(FNAME0, 0600)) == -1) {
174 perror("fstime: creat");
175 exit(1);
176 }
177 close(f);
178
179 if((g = creat(FNAME1, 0600)) == -1) {
180 perror("fstime: creat");
181 exit(1);
182 }
183 close(g);
184
185 if( (f = open(FNAME0, 2)) == -1) {
186 perror("fstime: open");
187 exit(1);
188 }
189 if( ( g = open(FNAME1, 2)) == -1 ) {
190 perror("fstime: open");
191 exit(1);
192 }
193
194 /* fill buffer */
195 for (i=0; i < bufsize; ++i)
196 buf[i] = i & 0xff;
197
198 signal(SIGKILL,clean_up);
199
200 /*
201 * Run the selected test.
202 * When I got here, this program ran full 30-second tests for
203 * write, read, and copy, outputting the results for each. BUT
204 * only the copy results are actually used in the benchmark index.
205 * With multiple iterations and three sets of FS tests, that amounted
206 * to about 10 minutes of wasted time per run.
207 *
208 * So, I've made the test selectable. Except that the read and write
209 * passes are used to create the test file and calibrate the rates used
210 * to tweak the results of the copy test. So, for copy tests, we do
211 * a few seconds of write and read to prime the pump.
212 *
213 * Note that this will also pull the file into the FS cache on any
214 * modern system prior to the copy test. Whether this is good or
215 * bad is a matter of perspective, but it's how it was when I got
216 * here.
217 *
218 * Ian Smith <johantheghost at yahoo period com> 21 Sep 2007
219 */
220 switch (test) {
221 case 'w':
222 status = w_test(seconds);
223 break;
224 case 'r':
225 w_test(2);
226 status = r_test(seconds);
227 break;
228 case 'c':
229 w_test(2);
230 r_test(2);
231 status = c_test(seconds);
232 break;
233 default:
234 fprintf(stderr, "fstime: unknown test \'%c\'\n", test);
235 exit(6);
236 }
237 if (status) {
238 clean_up();
239 exit(1);
240 }
241
242 clean_up();
243 exit(0);
244 }
245
246
getFloatTime()247 static double getFloatTime()
248 {
249 struct timeval t;
250
251 gettimeofday(&t, 0);
252 return (double) t.tv_sec + (double) t.tv_usec / 1000000.0;
253 }
254
255
256 /*
257 * Run the write test for the time given in seconds.
258 */
w_test(int timeSecs)259 int w_test(int timeSecs)
260 {
261 unsigned long counted = 0L;
262 unsigned long tmp;
263 long f_blocks;
264 double start, end;
265 extern int sigalarm;
266
267 /* Sync and let it settle */
268 sync();
269 sleep(2);
270 sync();
271 sleep(2);
272
273 /* Set an alarm. */
274 sigalarm = 0;
275 signal(SIGALRM, stop_count);
276 alarm(timeSecs);
277
278 start = getFloatTime();
279
280 while (!sigalarm) {
281 for(f_blocks=0; f_blocks < max_buffs; ++f_blocks) {
282 if ((tmp=write(f, buf, bufsize)) != bufsize) {
283 if (errno != EINTR) {
284 perror("fstime: write");
285 return(-1);
286 }
287 stop_count();
288 counted += ((tmp+HALFCOUNT)/COUNTSIZE);
289 } else
290 counted += count_per_buf;
291 }
292 lseek(f, 0L, 0); /* rewind */
293 }
294
295 /* stop clock */
296 end = getFloatTime();
297 write_score = (long) ((double) counted / ((end - start) * count_per_k));
298 printf("Write done: %ld in %.4f, score %ld\n",
299 counted, end - start, write_score);
300
301 /*
302 * Output the test results. Use the true time.
303 */
304 fprintf(stderr, "COUNT|%ld|0|KBps\n", write_score);
305 fprintf(stderr, "TIME|%.1f\n", end - start);
306
307 return(0);
308 }
309
310 /*
311 * Run the read test for the time given in seconds.
312 */
r_test(int timeSecs)313 int r_test(int timeSecs)
314 {
315 unsigned long counted = 0L;
316 unsigned long tmp;
317 double start, end;
318 extern int sigalarm;
319
320 /* Sync and let it settle */
321 sync();
322 sleep(2);
323 sync();
324 sleep(2);
325
326 /* rewind */
327 errno = 0;
328 lseek(f, 0L, 0);
329
330 /* Set an alarm. */
331 sigalarm = 0;
332 signal(SIGALRM, stop_count);
333 alarm(timeSecs);
334
335 start = getFloatTime();
336
337 while (!sigalarm) {
338 /* read while checking for an error */
339 if ((tmp=read(f, buf, bufsize)) != bufsize) {
340 switch(errno) {
341 case 0:
342 case EINVAL:
343 lseek(f, 0L, 0); /* rewind at end of file */
344 counted += (tmp+HALFCOUNT)/COUNTSIZE;
345 continue;
346 case EINTR:
347 stop_count();
348 counted += (tmp+HALFCOUNT)/COUNTSIZE;
349 break;
350 default:
351 perror("fstime: read");
352 return(-1);
353 break;
354 }
355 } else
356 counted += count_per_buf;
357 }
358
359 /* stop clock */
360 end = getFloatTime();
361 read_score = (long) ((double) counted / ((end - start) * count_per_k));
362 printf("Read done: %ld in %.4f, score %ld\n",
363 counted, end - start, read_score);
364
365 /*
366 * Output the test results. Use the true time.
367 */
368 fprintf(stderr, "COUNT|%ld|0|KBps\n", read_score);
369 fprintf(stderr, "TIME|%.1f\n", end - start);
370
371 return(0);
372 }
373
374
375 /*
376 * Run the copy test for the time given in seconds.
377 */
c_test(int timeSecs)378 int c_test(int timeSecs)
379 {
380 unsigned long counted = 0L;
381 unsigned long tmp;
382 double start, end;
383 extern int sigalarm;
384
385 sync();
386 sleep(2);
387 sync();
388 sleep(1);
389
390 /* rewind */
391 errno = 0;
392 lseek(f, 0L, 0);
393
394 /* Set an alarm. */
395 sigalarm = 0;
396 signal(SIGALRM, stop_count);
397 alarm(timeSecs);
398
399 start = getFloatTime();
400
401 while (!sigalarm) {
402 if ((tmp=read(f, buf, bufsize)) != bufsize) {
403 switch(errno) {
404 case 0:
405 case EINVAL:
406 lseek(f, 0L, 0); /* rewind at end of file */
407 lseek(g, 0L, 0); /* rewind the output too */
408 continue;
409 case EINTR:
410 /* part credit for leftover bytes read */
411 counted += ( (tmp * write_score) /
412 (read_score + write_score)
413 + HALFCOUNT) / COUNTSIZE;
414 stop_count();
415 break;
416 default:
417 perror("fstime: copy read");
418 return(-1);
419 break;
420 }
421 } else {
422 if ((tmp=write(g, buf, bufsize)) != bufsize) {
423 if (errno != EINTR) {
424 perror("fstime: copy write");
425 return(-1);
426 }
427 counted += (
428 /* Full credit for part of buffer written */
429 tmp +
430
431 /* Plus part credit having read full buffer */
432 ( ((bufsize - tmp) * write_score) /
433 (read_score + write_score) )
434 + HALFCOUNT) / COUNTSIZE;
435 stop_count();
436 } else
437 counted += count_per_buf;
438 }
439 }
440
441 /* stop clock */
442 end = getFloatTime();
443 copy_score = (long) ((double) counted / ((end - start) * count_per_k));
444 printf("Copy done: %ld in %.4f, score %ld\n",
445 counted, end - start, copy_score);
446
447 /*
448 * Output the test results. Use the true time.
449 */
450 fprintf(stderr, "COUNT|%ld|0|KBps\n", copy_score);
451 fprintf(stderr, "TIME|%.1f\n", end - start);
452
453 return(0);
454 }
455
stop_count(void)456 void stop_count(void)
457 {
458 extern int sigalarm;
459 sigalarm = 1;
460 }
461
clean_up(void)462 void clean_up(void)
463 {
464 unlink(FNAME0);
465 unlink(FNAME1);
466 }
467