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