--- /dev/null
+/* A simple file system workload generator.
+ *
+ * Reads and writes a number of files in the current working directory.
+ *
+ * Command-line arguments:
+ * File size (bytes)
+ * File count
+ * Write fraction (0.0 - 1.0)
+ * Threads
+ * Benchmark duration (seconds)
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+int opt_filesize, opt_filecount, opt_threads, opt_duration;
+double opt_writeratio;
+
+struct thread_state {
+ pthread_t thread;
+ pthread_mutex_t lock;
+ int thread_num;
+ int read_count, write_count;
+ int64_t read_time, write_time;
+};
+
+static int64_t start_time;
+
+#define MAX_THREADS 128
+struct thread_state threads[MAX_THREADS];
+
+int64_t now_hires()
+{
+ struct timespec time;
+
+ if (clock_gettime(CLOCK_REALTIME, &time) != 0) {
+ perror("clock_gettime");
+ return 0;
+ }
+
+ return (int64_t)(time.tv_sec) * 1000000000 + time.tv_nsec;
+}
+
+int get_random(int range)
+{
+ return random() % range;
+}
+
+void sleep_micros(int duration)
+{
+ struct timespec req;
+ if (duration <= 0)
+ return;
+
+ req.tv_sec = duration / 1000000;
+ req.tv_nsec = (duration % 1000000) * 1000;
+
+ while (nanosleep(&req, &req) < 0 && errno == EINTR)
+ ;
+}
+
+#if 0
+void bench_write()
+{
+ FILE *f = fopen("writetest", "wb");
+ if (f == NULL) {
+ perror("fopen");
+ return;
+ }
+
+ char buf[4096];
+ for (int i = 0; i < 16; i++) {
+ int64_t start, end;
+ start = now_hires();
+ rewind(f);
+ fwrite(buf, 1, sizeof(buf), f);
+ fflush(f);
+ end = now_hires();
+ printf("Pass %d: Time = %"PRIi64"\n", i, end - start);
+ }
+}
+#endif
+
+void benchmark_op(struct thread_state *ts)
+{
+ int64_t start, end;
+
+ start = now_hires();
+
+ char filename[64];
+ sprintf(filename, "t%d/%d", ts->thread_num, get_random(opt_filecount));
+
+ double r = get_random(1000000) / 1e6;
+
+ if (r >= opt_writeratio) {
+ /* Read */
+ FILE *f = fopen(filename, "rb");
+ if (f == NULL) {
+ perror("fopen");
+ return;
+ }
+
+ char buf[65536];
+ while (fread(buf, 1, sizeof(buf), f) > 0) { }
+ fclose(f);
+
+ end = now_hires();
+ pthread_mutex_lock(&ts->lock);
+ ts->read_count++;
+ ts->read_time += (end - start);
+ pthread_mutex_unlock(&ts->lock);
+ } else {
+ /* Write */
+ FILE *f = fopen(filename, "wb");
+ if (f == NULL) {
+ perror("fopen");
+ return;
+ }
+
+ char buf[65536];
+ int bytes_left = opt_filesize;
+ while (bytes_left > 0) {
+ size_t written = fwrite(buf, 1,
+ bytes_left < sizeof(buf)
+ ? bytes_left : sizeof(buf),
+ f);
+ if (ferror(f))
+ return;
+ bytes_left -= written;
+ }
+ fclose(f);
+
+ end = now_hires();
+ pthread_mutex_lock(&ts->lock);
+ ts->write_count++;
+ ts->write_time += (end - start);
+ pthread_mutex_unlock(&ts->lock);
+ }
+}
+
+void *benchmark_thread(void *arg)
+{
+ struct thread_state *ts = (struct thread_state *)arg;
+
+ while (1) {
+ benchmark_op(ts);
+ //sleep_micros(100000);
+ }
+
+ return NULL;
+}
+
+void launch_thread(int i)
+{
+ memset(&threads[i], 0, sizeof(struct thread_state));
+ threads[i].thread_num = i;
+ pthread_mutex_init(&threads[i].lock, NULL);
+ printf("Launching thread %d...\n", i);
+ if (pthread_create(&threads[i].thread, NULL, benchmark_thread, &threads[i]) != 0) {
+ fprintf(stderr, "Error launching thread!\n");
+ exit(1);
+ }
+}
+
+void wait_thread(int n)
+{
+ void *result;
+ pthread_join(threads[n].thread, &result);
+}
+
+void reset_stats(int print)
+{
+ int read_count = 0, write_count = 0;
+ int64_t read_time = 0, write_time = 0;
+
+ for (int i = 0; i < opt_threads; i++) {
+ pthread_mutex_lock(&threads[i].lock);
+ read_count += threads[i].read_count;
+ write_count += threads[i].write_count;
+ read_time += threads[i].read_time;
+ write_time += threads[i].write_time;
+ threads[i].read_count = threads[i].write_count = 0;
+ threads[i].read_time = threads[i].write_time = 0;
+ pthread_mutex_unlock(&threads[i].lock);
+ }
+
+ if (print) {
+ printf("Reads: %d operations, %f average latency\n",
+ read_count, read_time / 1e9 / read_count);
+ printf("Writes: %d operations, %f average latency\n",
+ write_count, write_time / 1e9 / write_count);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 6) {
+ fprintf(stderr, "Usage: TODO\n");
+ return 1;
+ }
+
+ opt_filesize = atoi(argv[1]);
+ opt_filecount = atoi(argv[2]);
+ opt_writeratio = atof(argv[3]);
+ opt_threads = atoi(argv[4]);
+ opt_duration = atoi(argv[5]);
+
+ srandom(time(NULL));
+
+ start_time = now_hires();
+ printf("Testing with %d threads\n", opt_threads);
+
+ for (int i = 0; i < opt_threads; i++) {
+ launch_thread(i);
+ }
+
+ /* Warm up phase: run for half the time but do not keep statistics */
+ sleep_micros(opt_duration * 1000000 / 2);
+ reset_stats(1);
+
+ /* Benchmark phase: run for half the time and print summary */
+ sleep_micros(opt_duration * 1000000 / 2);
+ reset_stats(1);
+
+ return 0;
+}