nicer formatting
[tridge/junkcode.git] / threadio.c
1 /*
2   simulate IO and thread pattern in ntap sio test
3
4   Copyright (C) Andrew Tridgell <tridge@samba.org> 2007
5
6   Released under the GNU GPL version 2 or later
7 */
8
9 #define _GNU_SOURCE
10 #define _FILE_OFFSET_BITS 64
11 #define _LARGEFILE64_SOURCE
12 #define _LARGE_FILES
13 #include <stdio.h>
14 #include <unistd.h>
15 #include <stdlib.h>
16 #include <time.h>
17 #include <getopt.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <sys/stat.h>
22 #include <fcntl.h>
23 #include <errno.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <dirent.h>
27 #include <pthread.h>
28 #include <stdbool.h>
29
30 static size_t block_size = 8192;
31 static off_t file_size = 1024*1024*(1024LL);
32 static unsigned num_threads = 1;
33 static const char *fname;
34 static volatile unsigned *io_count;
35 static volatile int run_finished;
36 static unsigned runtime = 20;
37 static bool sync_io;
38 static bool direct_io;
39 static int flush_blocks;
40 static bool one_fd;
41 static bool reopen;
42
43 static int shared_fd;
44
45 static int open_file(int id) 
46 {
47         unsigned flags = O_RDWR|O_CREAT|O_LARGEFILE;
48         int fd;
49         char *name;
50
51         if (sync_io) flags |= O_SYNC;
52         if (direct_io) flags |= O_DIRECT;
53         if (id != -1 && strchr(fname, '%')) {
54                 asprintf(&name, fname, id);
55         } else {
56                 name = strdup(fname);
57         }
58         fd = open(name, flags, 0644);
59         if (fd == -1) {
60                 perror(fname);
61                 exit(1);
62         }
63         free(name);
64         return fd;
65 }
66
67 static void run_child(int id)
68 {
69         unsigned num_blocks = file_size / block_size;
70         char *buf;
71         int fd;
72
73         if (!one_fd && !reopen) {
74                 fd = open_file(id);
75         } else {
76                 fd = shared_fd;
77         }
78
79         buf = malloc(block_size);
80         memset(buf, 1, block_size);
81         
82         srandom(id ^ random());
83
84         while (!run_finished) {
85                 unsigned offset = random() % num_blocks;
86                 if (reopen) {
87                         fd = open_file(id);
88                 }
89                 if (pwrite(fd, buf, block_size, offset*(off_t)block_size) != block_size) {
90                         perror("pwrite");
91                         printf("pwrite failed!\n");
92                         exit(1);
93                 }
94                 io_count[id]++;
95                 if (flush_blocks && io_count[id] % flush_blocks == 0) {
96                         fsync(fd);
97                 }
98                 if (reopen) {
99                         close(fd);
100                 }
101         }
102
103         close(fd);
104         return;
105 }
106
107 /*
108   create a thread with initial function fn(private)
109 */
110 static pthread_t thread_start(void (*fn)(int), int id)
111 {
112         pthread_t thread_id;
113         pthread_attr_t thread_attr;
114         int rc;
115         typedef void *(*thread_fn_t)(void *);
116         pthread_attr_init(&thread_attr);
117         pthread_attr_setdetachstate(&thread_attr, 0);
118         rc = pthread_create(&thread_id, &thread_attr, (thread_fn_t)fn, (void *)id);
119         pthread_attr_destroy(&thread_attr);
120
121         if (rc != 0) {
122                 fprintf(stderr,"Thread create failed for id %d\n", id);
123                 exit(1);
124         }
125
126         return thread_id;
127 }
128
129 /* wait for a thread to exit */
130 static int thread_join(pthread_t id)
131 {
132         return pthread_join(id, NULL);
133 }
134
135
136 static void run_test(void)
137 {
138         int i;
139         pthread_t *tids;
140         time_t started = 0;
141         unsigned warmup_count=0;
142         unsigned total;
143
144         tids = calloc(num_threads, sizeof(pthread_t));
145         io_count = calloc(num_threads, sizeof(unsigned));
146
147         if (one_fd) {
148                 shared_fd = open_file(-1);
149         }
150
151
152         for (i=0;i<num_threads;i++) {
153                 tids[i] = thread_start(run_child, i);
154         }
155         
156         while (1) {
157                 int in_warmup=0;
158                 time_t t = time(NULL);
159                 total = 0;
160
161                 sleep(1);
162                 if (started == 0) {
163                         in_warmup = 1;
164                         started = t;
165                 }
166                 for (i=0;i<num_threads;i++) {
167                         unsigned count = io_count[i];
168                         printf("%6u ", count);
169                         if (count == 0) {
170                                 started = 0;
171                         }
172                         total += count;
173                 }
174                 printf("    total: %6u ", total);
175                 if (started != 0 && started != t) {
176                         printf(" %3u seconds  %.0f kbytes/s\n", 
177                                (unsigned)(t - started),
178                                block_size*(total-warmup_count)/(1024.0*(t-started)));
179                 } else {
180                         printf("warmup\n");
181                 }
182                 if (started != 0) {
183                         if (in_warmup) {
184                                 printf("Starting run\n");
185                                 warmup_count = total;
186                         }
187                         if (time(NULL) - started >= runtime) {
188                                 break;
189                         }
190                 }
191         }
192
193         run_finished = 1;
194         printf("Run finished - cleaning up\n");
195         for (i=0;i<num_threads;i++) {
196                 thread_join(tids[i]);
197                 printf("finished thread %d\n", i);
198         }
199
200         printf("Throughput %.0f kbytes/s\n", 
201                block_size*(total-warmup_count)/(1024.0*runtime));
202 }
203
204 static void usage(void)
205 {
206         printf("Usage: threadio <options> <filename>\n");
207         printf("Options:\n");
208         printf(" -b <size>     block size\n");
209         printf(" -f <size>     file size\n");
210         printf(" -n <number>   number of threads\n");   
211         printf(" -t <time>     runtime in seconds\n");
212         printf(" -F <numios>   fsync every numios blocks per thread\n");
213         printf(" -S            use O_SYNC on open\n");
214         printf(" -D            use O_DIRECT on open\n");
215         printf(" -R            reopen the file on every write\n");
216         printf(" -1            use one file descriptor for all threads\n");
217         printf("\nNote: when not using -1, filename may contain %%d for thread id\n");
218 }
219
220
221 int main(int argc, char * const argv[])
222 {
223         int opt;
224
225         setlinebuf(stdout);
226         
227         while ((opt = getopt(argc, argv, "b:f:n:t:1F:SDsR")) != -1) {
228                 switch (opt) {
229                 case 'b':
230                         block_size = strtoul(optarg, NULL, 0);
231                         break;
232                 case 'f':
233                         file_size = strtoull(optarg, NULL, 0);
234                         break;
235                 case 'n':
236                         num_threads = strtoul(optarg, NULL, 0);
237                         break;
238                 case 't':
239                         runtime = strtoul(optarg, NULL, 0);
240                         break;
241                 case 'S':
242                         sync_io = true;
243                         break;
244                 case 'D':
245                         direct_io = true;
246                         break;
247                 case 'F':
248                         flush_blocks = strtoul(optarg, NULL, 0);
249                         break;
250                 case '1':
251                         one_fd = true;
252                         break;
253                 case 'R':
254                         reopen = true;
255                         break;
256                 default:
257                         printf("Invalid option '%c'\n", opt);
258                         usage();
259                         exit(1);
260                 }
261         }
262
263         argv += optind;
264         argc -= optind;
265
266         if (argc < 1) {
267                 usage();
268                 exit(1);
269         }
270
271         fname = argv[0];
272
273         srandom(time(NULL));
274
275         run_test();
276         return 0;
277 }