Add a new more general-purpose microbenchmark tool in C.
authorMichael Vrable <mvrable@cs.ucsd.edu>
Mon, 7 Feb 2011 13:58:01 +0000 (05:58 -0800)
committerMichael Vrable <mvrable@cs.ucsd.edu>
Mon, 7 Feb 2011 13:58:01 +0000 (05:58 -0800)
.gitignore
microbench/CMakeLists.txt
microbench/mixedbench.c [new file with mode: 0644]

index a8e1b2e..41e966b 100644 (file)
@@ -13,6 +13,7 @@ kvstore/kvstore
 logbench/logbench
 microbench/bench
 microbench/lockmem
+microbench/mixedbench
 microbench/readbench
 microbench/statbench
 nfs3/nfsproxy
index deadde4..33b4bd7 100644 (file)
@@ -7,6 +7,9 @@ target_link_libraries(readbench pthread rt)
 add_executable(statbench statbench.c)
 target_link_libraries(statbench pthread rt)
 
+add_executable(mixedbench mixedbench.c)
+target_link_libraries(mixedbench pthread rt)
+
 add_executable(lockmem lockmem.c)
 
 set(CMAKE_C_FLAGS "-Wall -std=gnu99 ${CMAKE_C_FLAGS}")
diff --git a/microbench/mixedbench.c b/microbench/mixedbench.c
new file mode 100644 (file)
index 0000000..fcb9f63
--- /dev/null
@@ -0,0 +1,235 @@
+/* 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;
+}