1 /* A simple file system workload generator.
3 * Reads and writes a number of files in the current working directory.
5 * Command-line arguments:
8 * Write fraction (0.0 - 1.0)
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 * Directory size (number of files per numbered subdirectory)
14 * Read/write block size (0 for the entire file)
25 #include <sys/types.h>
30 int opt_filesize, opt_filecount, opt_threads, opt_duration, opt_intervals, opt_dirsize, opt_blocksize;
31 double opt_writeratio, opt_ops;
39 int read_count, write_count;
40 double read_time, write_time, read_time2, write_time2;
43 static int64_t start_time;
45 #define MAX_THREADS 128
46 struct thread_state threads[MAX_THREADS];
48 static double sq(double x)
53 static double stddev(double x, double x2, int n)
57 return sqrt((x2 / n - sq(x / n)) * n / (n - 1));
64 if (clock_gettime(CLOCK_REALTIME, &time) != 0) {
65 perror("clock_gettime");
69 return (int64_t)(time.tv_sec) * 1000000000 + time.tv_nsec;
72 int get_random(int range)
74 return random() % range;
77 void sleep_micros(int duration)
83 req.tv_sec = duration / 1000000;
84 req.tv_nsec = (duration % 1000000) * 1000;
86 while (nanosleep(&req, &req) < 0 && errno == EINTR)
90 void benchmark_op(struct thread_state *ts)
96 /* The space of all files is partitioned evenly based on the number of
97 * threads. Pick a file out of our particular partition. */
98 int thread_num, thread_count;
99 if (ts->thread_num >= write_threads) {
101 thread_num = ts->thread_num - write_threads;
102 thread_count = opt_threads - write_threads;
105 thread_num = ts->thread_num;
106 thread_count = write_threads;
109 int n = get_random(opt_filecount / thread_count);
110 n += thread_num * (opt_filecount / thread_count);
111 int n1 = n / opt_dirsize, n2 = n % opt_dirsize;
113 sprintf(filename, "%d/%d", n1, n2);
115 /* If a smaller blocksize was requested, choose a random offset within the
118 if (opt_blocksize > 0) {
119 offset = get_random(opt_filesize / opt_blocksize) * opt_blocksize;
122 if (ts->thread_num >= write_threads) {
124 FILE *f = fopen(filename, "rb");
126 fprintf(stderr, "fopen(%s): %m\n", filename);
131 fseek(f, offset, SEEK_SET);
134 int bytes_left = opt_blocksize > 0 ? opt_blocksize : opt_filesize;
135 while (bytes_left > 0) {
136 size_t read = fread(buf, 1, bytes_left < sizeof(buf)
137 ? bytes_left : sizeof(buf), f);
145 pthread_mutex_lock(&ts->lock);
147 ts->read_time += (end - start) / 1e9;
148 ts->read_time2 += sq((end - start) / 1e9);
149 pthread_mutex_unlock(&ts->lock);
152 FILE *f = fopen(filename, "r+b");
154 fprintf(stderr, "fopen(%s): %m\n", filename);
159 fseek(f, offset, SEEK_SET);
162 int bytes_left = opt_blocksize > 0 ? opt_blocksize : opt_filesize;
163 while (bytes_left > 0) {
164 size_t written = fwrite(buf, 1,
165 bytes_left < sizeof(buf)
166 ? bytes_left : sizeof(buf),
170 bytes_left -= written;
175 pthread_mutex_lock(&ts->lock);
177 ts->write_time += (end - start) / 1e9;
178 ts->write_time2 += sq((end - start) / 1e9);
179 pthread_mutex_unlock(&ts->lock);
183 void *benchmark_thread(void *arg)
185 struct thread_state *ts = (struct thread_state *)arg;
187 int target_delay = (opt_threads / opt_ops) * 1e6;
190 int64_t start = now_hires();
192 int64_t end = now_hires();
194 int elapsed = (end - start) / 1000;
195 if (elapsed < target_delay)
196 sleep_micros(target_delay - elapsed);
202 void launch_thread(int i)
204 memset(&threads[i], 0, sizeof(struct thread_state));
205 threads[i].thread_num = i;
206 pthread_mutex_init(&threads[i].lock, NULL);
207 if (pthread_create(&threads[i].thread, NULL, benchmark_thread, &threads[i]) != 0) {
208 fprintf(stderr, "Error launching thread!\n");
213 void wait_thread(int n)
216 pthread_join(threads[n].thread, &result);
219 void reset_stats(int print, double duration)
221 int read_count = 0, write_count = 0;
222 double read_time = 0, write_time = 0, read_time2 = 0, write_time2 = 0;
224 for (int i = 0; i < opt_threads; i++) {
225 pthread_mutex_lock(&threads[i].lock);
226 read_count += threads[i].read_count;
227 write_count += threads[i].write_count;
228 read_time += threads[i].read_time;
229 write_time += threads[i].write_time;
230 read_time2 += threads[i].read_time2;
231 write_time2 += threads[i].write_time2;
232 threads[i].read_count = threads[i].write_count = 0;
233 threads[i].read_time = threads[i].write_time = 0;
234 threads[i].read_time2 = threads[i].write_time2 = 0;
235 pthread_mutex_unlock(&threads[i].lock);
239 printf("read: [%g, %f, %f]\n",
240 read_count / duration, read_time / read_count,
241 stddev(read_time, read_time2, read_count));
242 printf("write: [%g, %f, %f]\n",
243 write_count / duration, write_time / write_count,
244 stddev(write_time, write_time2, write_count));
250 int main(int argc, char *argv[])
253 fprintf(stderr, "Usage: TODO\n");
257 opt_filesize = atoi(argv[1]);
258 opt_filecount = atoi(argv[2]);
259 opt_writeratio = atof(argv[3]);
260 opt_threads = atoi(argv[4]);
261 opt_duration = atoi(argv[5]);
262 opt_ops = atof(argv[6]);
263 opt_intervals = atoi(argv[7]);
264 opt_dirsize = atoi(argv[8]);
265 opt_blocksize = atoi(argv[9]);
269 start_time = now_hires();
271 /* Partition threads into those that should do reads and those for writes,
272 * as close as possible to the desired allocation. */
273 write_threads = (int)round(opt_threads * opt_writeratio);
274 fprintf(stderr, "Using %d threads for reads, %d for writes\n",
275 opt_threads - write_threads, write_threads);
277 for (int i = 0; i < opt_threads; i++) {
281 for (int i = 0; i < opt_intervals; i++) {
282 sleep_micros(opt_duration * 1000000 / opt_intervals);
283 reset_stats(1, (double)opt_duration / opt_intervals);