A few more updates to the mixed read/write microbenchmark.
[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  *   Target operations per second (aggregate across all threads)
12  */
13
14 #include <errno.h>
15 #include <inttypes.h>
16 #include <pthread.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <time.h>
24 #include <unistd.h>
25 #include <math.h>
26
27 int opt_filesize, opt_filecount, opt_threads, opt_duration;
28 double opt_writeratio, opt_ops;
29
30 struct thread_state {
31     pthread_t thread;
32     pthread_mutex_t lock;
33     int thread_num;
34     int read_count, write_count;
35     double read_time, write_time, read_time2, write_time2;
36 };
37
38 static int64_t start_time;
39
40 #define MAX_THREADS 128
41 struct thread_state threads[MAX_THREADS];
42
43 static double sq(double x)
44 {
45     return x * x;
46 }
47
48 static double stddev(double x, double x2, int n)
49 {
50     if (n < 2)
51         return 0;
52     return sqrt((x2 / n - sq(x / n)) * n / (n - 1));
53 }
54
55 int64_t now_hires()
56 {
57     struct timespec time;
58
59     if (clock_gettime(CLOCK_REALTIME, &time) != 0) {
60         perror("clock_gettime");
61         return 0;
62     }
63
64     return (int64_t)(time.tv_sec) * 1000000000 + time.tv_nsec;
65 }
66
67 int get_random(int range)
68 {
69     return random() % range;
70 }
71
72 void sleep_micros(int duration)
73 {
74     struct timespec req;
75     if (duration <= 0)
76         return;
77
78     req.tv_sec = duration / 1000000;
79     req.tv_nsec = (duration % 1000000) * 1000;
80
81     while (nanosleep(&req, &req) < 0 && errno == EINTR)
82         ;
83 }
84
85 void benchmark_op(struct thread_state *ts)
86 {
87     int64_t start, end;
88
89     start = now_hires();
90
91     char filename[64];
92     sprintf(filename, "t%d/%d", ts->thread_num, get_random(opt_filecount));
93
94     double r = get_random(1000000) / 1e6;
95
96     if (r >= opt_writeratio) {
97         /* Read */
98         FILE *f = fopen(filename, "rb");
99         if (f == NULL) {
100             perror("fopen");
101             return;
102         }
103
104         char buf[65536];
105         while (fread(buf, 1, sizeof(buf), f) > 0) { }
106         fclose(f);
107
108         end = now_hires();
109         pthread_mutex_lock(&ts->lock);
110         ts->read_count++;
111         ts->read_time += (end - start) / 1e9;
112         ts->read_time2 += sq((end - start) / 1e9);
113         pthread_mutex_unlock(&ts->lock);
114     } else {
115         /* Write */
116         FILE *f = fopen(filename, "wb");
117         if (f == NULL) {
118             perror("fopen");
119             return;
120         }
121
122         char buf[65536];
123         int bytes_left = opt_filesize;
124         while (bytes_left > 0) {
125             size_t written = fwrite(buf, 1,
126                                     bytes_left < sizeof(buf)
127                                      ? bytes_left : sizeof(buf),
128                                     f);
129             if (ferror(f))
130                 return;
131             bytes_left -= written;
132         }
133         fclose(f);
134
135         end = now_hires();
136         pthread_mutex_lock(&ts->lock);
137         ts->write_count++;
138         ts->write_time += (end - start) / 1e9;
139         ts->write_time2 += sq((end - start) / 1e9);
140         pthread_mutex_unlock(&ts->lock);
141     }
142 }
143
144 void *benchmark_thread(void *arg)
145 {
146     struct thread_state *ts = (struct thread_state *)arg;
147
148     int target_delay = (opt_threads / opt_ops) * 1e6;
149
150     while (1) {
151         int64_t start = now_hires();
152         benchmark_op(ts);
153         int64_t end = now_hires();
154
155         int elapsed = (end - start) / 1000;
156         if (elapsed < target_delay)
157             sleep_micros(target_delay - elapsed);
158     }
159
160     return NULL;
161 }
162
163 void launch_thread(int i)
164 {
165     memset(&threads[i], 0, sizeof(struct thread_state));
166     threads[i].thread_num = i;
167     pthread_mutex_init(&threads[i].lock, NULL);
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, double duration)
181 {
182     int read_count = 0, write_count = 0;
183     double read_time = 0, write_time = 0, read_time2 = 0, write_time2 = 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         read_time2 += threads[i].read_time2;
192         write_time2 += threads[i].write_time2;
193         threads[i].read_count = threads[i].write_count = 0;
194         threads[i].read_time = threads[i].write_time = 0;
195         pthread_mutex_unlock(&threads[i].lock);
196     }
197
198     if (print) {
199         printf("read: [%g, %f, %f]\n",
200                read_count / duration, read_time / read_count,
201                stddev(read_time, read_time2, read_count));
202         printf("write: [%g, %f, %f]\n",
203                write_count / duration, write_time / write_count,
204                stddev(write_time, write_time2, write_count));
205         printf("\n");
206     }
207 }
208
209 int main(int argc, char *argv[])
210 {
211     if (argc != 7) {
212         fprintf(stderr, "Usage: TODO\n");
213         return 1;
214     }
215
216     opt_filesize = atoi(argv[1]);
217     opt_filecount = atoi(argv[2]);
218     opt_writeratio = atof(argv[3]);
219     opt_threads = atoi(argv[4]);
220     opt_duration = atoi(argv[5]);
221     opt_ops = atof(argv[6]);
222
223     srandom(time(NULL));
224
225     start_time = now_hires();
226
227     for (int i = 0; i < opt_threads; i++) {
228         launch_thread(i);
229     }
230
231     for (int i = 0; i < 4; i++) {
232         sleep_micros(opt_duration * 1000000 / 4);
233         reset_stats(1, opt_duration / 4.0);
234     }
235
236     return 0;
237 }