From: Michael Vrable Date: Mon, 7 Feb 2011 13:58:01 +0000 (-0800) Subject: Add a new more general-purpose microbenchmark tool in C. X-Git-Url: https://git.vrable.net/?a=commitdiff_plain;h=baa91fc1fe45620968f653e9e2ee04e48d56ebab;p=bluesky.git Add a new more general-purpose microbenchmark tool in C. --- diff --git a/.gitignore b/.gitignore index a8e1b2e..41e966b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ kvstore/kvstore logbench/logbench microbench/bench microbench/lockmem +microbench/mixedbench microbench/readbench microbench/statbench nfs3/nfsproxy diff --git a/microbench/CMakeLists.txt b/microbench/CMakeLists.txt index deadde4..33b4bd7 100644 --- a/microbench/CMakeLists.txt +++ b/microbench/CMakeLists.txt @@ -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 index 0000000..fcb9f63 --- /dev/null +++ b/microbench/mixedbench.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +}