#! /usr/bin/C #option -p -cWall -cO2 -llpthread #include #include #include #include #include #include #include #include #include #include static unsigned long block_size, file_size; static unsigned concurrency, loop_count; static enum { m_undef, m_read, m_write } mode; static int use_fdatasync; static int fd; static double now() { timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec + (double)tv.tv_usec / 1000000; } static void* do_bench(void*) { char* buf = (char*)memalign(block_size, block_size); for (unsigned i = 0; i < loop_count; i++) { size_t off = ((size_t)random() * block_size) % file_size; if (mode == m_read) { ssize_t l = pread(fd, buf, block_size, off); assert(l == (ssize_t)block_size); } else { ssize_t l = pwrite(fd, buf, block_size, off); assert(l == (ssize_t)block_size); if (use_fdatasync) { fdatasync(fd); } } } free(buf); return NULL; } static void usage() { fprintf(stderr, "Usage: randombench.cc test-file\n" "Options: -b size block size (KB)\n" " -c threads concurrency\n" " -f size file size (KB)\n" " -l count loop count\n" " -m mode mode (either read or write)\n" " -s call fdatasync after each write\n"); exit(1); } int main(int argc, char** argv) { { // parse args int ch; while ((ch = getopt(argc, argv, "b:c:f:l:m:s")) != -1) { switch (ch) { case 'b': if (sscanf(optarg, "%lu", &block_size) != 1) { fprintf(stderr, "invalid -b option\n"); exit(1); } block_size *= 1024; break; case 'c': if (sscanf(optarg, "%u", &concurrency) != 1) { fprintf(stderr, "invalid -u option\n"); exit(1); } break; case 'f': if (sscanf(optarg, "%lu", &file_size) != 1) { fprintf(stderr, "invalid -f option\n"); exit(1); } file_size *= 1024; break; case 'l': if (sscanf(optarg, "%u", &loop_count) != 1) { fprintf(stderr, "invalid -l option\n"); exit(1); } break; case 'm': if (strcmp(optarg, "read") == 0) { mode = m_read; } else if (strcmp(optarg, "write") == 0) { mode = m_write; } else { fprintf(stderr, "invalid -m option\n"); exit(1); } break; case 's': use_fdatasync = 1; break; default: usage(); break; } } if (block_size == 0 || file_size == 0 || concurrency == 0 || file_size % block_size != 0) { usage(); } argc -= optind; argv += optind; if (argc != 1) { usage(); } } // build test file if ( #ifdef O_DIRECT (fd = open(argv[0], O_RDWR | O_CREAT | O_TRUNC | O_DIRECT, 0666)) == -1 #elif defined(DIRECTIO_ON) (fd = open(argv[0], O_RDWR | O_CREAT | O_TRUNC, 0666)) == -1 #else # error "not linux nor solaris" #endif ) { fprintf(stderr, "failed to open file:%s:%s\n", argv[0], strerror(errno)); exit(2); } #ifdef DIRECTIO_ON if (directio(fd, DIRECTIO_ON) == -1) { fprintf(stderr, "warning: %s\n", strerror(errno)); } #endif { void* buf = memalign(block_size, block_size * 64); for (unsigned long off = 0; off < file_size + 16*1024*1024; off += block_size * 64) { if (write(fd, buf, block_size * 64) != (ssize_t)block_size * 64) { perror("failed to build test file"); exit(3); } } free(buf); } unlink(argv[0]); // remove test file on exit // spawn tester threads and wait pthread_t* threads = new pthread_t [concurrency]; double start = now(); for (unsigned i = 0; i < concurrency; i++) { if (pthread_create(threads + i, NULL, do_bench, &i) != 0) { perror("failed to spawn tester thread"); } } for (unsigned i = 0; i < concurrency; i++) { pthread_join(threads[i], NULL); } double elapsed = now() - start; printf("block size: %lu KB\n" "file size: %lu KB\n" "cuncurrency: %u threads\n" "elapsed: %.3f seconds (%.3f tps)\n" "throughput: %.3f MB/sec.\n", block_size / 1024, file_size / 1024, concurrency, elapsed, concurrency * loop_count / elapsed, block_size * concurrency * loop_count / elapsed / 1024 / 1024); return 0; }