Changes to output formattng of mixedbench.
[bluesky.git] / microbench / mixedbench.c
1 /* A simple file system workload generator.
2  *
3  * Reads and writes a number of files in the current working directory.
4  *
5  * Command-line arguments:
6  *   File size (bytes)
7  *   File count
8  *   Write fraction (0.0 - 1.0)
9  *   Threads
10  *   Benchmark duration (seconds)
11  */
12
13 #include <errno.h>
14 #include <inttypes.h>
15 #include <pthread.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #include <time.h>
23 #include <unistd.h>
24
25 int opt_filesize, opt_filecount, opt_threads, opt_duration;
26 double opt_writeratio;
27
28 struct thread_state {
29     pthread_t thread;
30     pthread_mutex_t lock;
31     int thread_num;
32     int read_count, write_count;
33     int64_t read_time, write_time;
34 };
35
36 static int64_t start_time;
37
38 #define MAX_THREADS 128
39 struct thread_state threads[MAX_THREADS];
40
41 int64_t now_hires()
42 {
43     struct timespec time;
44
45     if (clock_gettime(CLOCK_REALTIME, &time) != 0) {
46         perror("clock_gettime");
47         return 0;
48     }
49
50     return (int64_t)(time.tv_sec) * 1000000000 + time.tv_nsec;
51 }
52
53 int get_random(int range)
54 {
55     return random() % range;
56 }
57
58 void sleep_micros(int duration)
59 {
60     struct timespec req;
61     if (duration <= 0)
62         return;
63
64     req.tv_sec = duration / 1000000;
65     req.tv_nsec = (duration % 1000000) * 1000;
66
67     while (nanosleep(&req, &req) < 0 && errno == EINTR)
68         ;
69 }
70
71 #if 0
72 void bench_write()
73 {
74     FILE *f = fopen("writetest", "wb");
75     if (f == NULL) {
76         perror("fopen");
77         return;
78     }
79
80     char buf[4096];
81     for (int i = 0; i < 16; i++) {
82         int64_t start, end;
83         start = now_hires();
84         rewind(f);
85         fwrite(buf, 1, sizeof(buf), f);
86         fflush(f);
87         end = now_hires();
88         printf("Pass %d: Time = %"PRIi64"\n", i, end - start);
89     }
90 }
91 #endif
92
93 void benchmark_op(struct thread_state *ts)
94 {
95     int64_t start, end;
96
97     start = now_hires();
98
99     char filename[64];
100     sprintf(filename, "t%d/%d", ts->thread_num, get_random(opt_filecount));
101
102     double r = get_random(1000000) / 1e6;
103
104     if (r >= opt_writeratio) {
105         /* Read */
106         FILE *f = fopen(filename, "rb");
107         if (f == NULL) {
108             perror("fopen");
109             return;
110         }
111
112         char buf[65536];
113         while (fread(buf, 1, sizeof(buf), f) > 0) { }
114         fclose(f);
115
116         end = now_hires();
117         pthread_mutex_lock(&ts->lock);
118         ts->read_count++;
119         ts->read_time += (end - start);
120         pthread_mutex_unlock(&ts->lock);
121     } else {
122         /* Write */
123         FILE *f = fopen(filename, "wb");
124         if (f == NULL) {
125             perror("fopen");
126             return;
127         }
128
129         char buf[65536];
130         int bytes_left = opt_filesize;
131         while (bytes_left > 0) {
132             size_t written = fwrite(buf, 1,
133                                     bytes_left < sizeof(buf)
134                                      ? bytes_left : sizeof(buf),
135                                     f);
136             if (ferror(f))
137                 return;
138             bytes_left -= written;
139         }
140         fclose(f);
141
142         end = now_hires();
143         pthread_mutex_lock(&ts->lock);
144         ts->write_count++;
145         ts->write_time += (end - start);
146         pthread_mutex_unlock(&ts->lock);
147     }
148 }
149
150 void *benchmark_thread(void *arg)
151 {
152     struct thread_state *ts = (struct thread_state *)arg;
153
154     while (1) {
155         benchmark_op(ts);
156         //sleep_micros(100000);
157     }
158
159     return NULL;
160 }
161
162 void launch_thread(int i)
163 {
164     memset(&threads[i], 0, sizeof(struct thread_state));
165     threads[i].thread_num = i;
166     pthread_mutex_init(&threads[i].lock, NULL);
167     printf("Launching thread %d...\n", i);
168     if (pthread_create(&threads[i].thread, NULL, benchmark_thread, &threads[i]) != 0) {
169         fprintf(stderr, "Error launching thread!\n");
170         exit(1);
171     }
172 }
173
174 void wait_thread(int n)
175 {
176     void *result;
177     pthread_join(threads[n].thread, &result);
178 }
179
180 void reset_stats(int print)
181 {
182     int read_count = 0, write_count = 0;
183     int64_t read_time = 0, write_time = 0;
184
185     for (int i = 0; i < opt_threads; i++) {
186         pthread_mutex_lock(&threads[i].lock);
187         read_count += threads[i].read_count;
188         write_count += threads[i].write_count;
189         read_time += threads[i].read_time;
190         write_time += threads[i].write_time;
191         threads[i].read_count = threads[i].write_count = 0;
192         threads[i].read_time = threads[i].write_time = 0;
193         pthread_mutex_unlock(&threads[i].lock);
194     }
195
196     if (print) {
197         printf("read: [%d, %f]\n",
198                read_count, read_time / 1e9 / read_count);
199         printf("write: [%d, %f]\n",
200                write_count, write_time / 1e9 / write_count);
201         printf("\n");
202     }
203 }
204
205 int main(int argc, char *argv[])
206 {
207     if (argc != 6) {
208         fprintf(stderr, "Usage: TODO\n");
209         return 1;
210     }
211
212     opt_filesize = atoi(argv[1]);
213     opt_filecount = atoi(argv[2]);
214     opt_writeratio = atof(argv[3]);
215     opt_threads = atoi(argv[4]);
216     opt_duration = atoi(argv[5]);
217
218     srandom(time(NULL));
219
220     start_time = now_hires();
221     printf("Testing with %d threads\n", opt_threads);
222
223     for (int i = 0; i < opt_threads; i++) {
224         launch_thread(i);
225     }
226
227     for (int i = 0; i < 4; i++) {
228         sleep_micros(opt_duration * 1000000 / 4);
229         reset_stats(1);
230     }
231
232     return 0;
233 }