diff --git a/Makefile b/Makefile index bb1adef..2151a58 100644 --- a/Makefile +++ b/Makefile @@ -42,13 +42,13 @@ tokyocabinettest: tokyocabinettest.c testutil.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< testutil.o -ltokyocabinet berkeleydbtest: berkeleydbtest.c testutil.o - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< testutil.o -ldb + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< testutil.o -ldb -ltokyocabinet tokyotyranttest: tokyotyranttest.c testutil.o $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< testutil.o -ltokyotyrant -ltokyocabinet kyototycoontest: kyototycoontest.cc testutil.o - $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< testutil.o -lkyototycoon + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ $< testutil.o -lkyototycoon -ltokyocabinet clean: -rm -f $(TARGETS) *.o diff --git a/berkeleydbtest.c b/berkeleydbtest.c index cf146e0..3c955cf 100644 --- a/berkeleydbtest.c +++ b/berkeleydbtest.c @@ -395,6 +395,12 @@ static void getlist_test(void *db, const char *command, int num, int vsiz, die("getlist_test is not implemented"); } +static void rangeout_test(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed) +{ + die("rangeout_test is not implemented"); +} + static void range_test(void *db, const char *command, int num, int vsiz, int batch, unsigned int seed) { @@ -447,7 +453,7 @@ static void range_test(void *db, const char *command, int num, int vsiz, free(data.data); flags = DB_NEXT; } - if (i != num) + if (debug && i != num) die("Unexpected record num"); cursor->close(cursor); @@ -463,8 +469,10 @@ struct benchmark_config config = { .num = 5000000, .vsiz = 100, .batch = 1000, - .thnum = 1, + .producer_thnum = 1, + .consumer_thnum = 1, .debug = false, + .verbose = 1, .share = 1, .ops = { .open_db = open_db, @@ -475,6 +483,7 @@ struct benchmark_config config = { .fwmkeys_test = fwmkeys_test, .getlist_test = getlist_test, .range_test = range_test, + .rangeout_test = rangeout_test, .outlist_test = outlist_test, }, }; diff --git a/kyototycoontest.cc b/kyototycoontest.cc index f04f022..6eed66f 100644 --- a/kyototycoontest.cc +++ b/kyototycoontest.cc @@ -66,6 +66,32 @@ static void get_test(void *db, int num, int vsiz, unsigned int seed) } } +static void putlist_bin_test(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed) +{ + RemoteDB *rdb = (RemoteDB *)db; + struct keygen keygen; + string value(vsiz, '\0'); + vector bulkrecs; + int i; + + keygen_init(&keygen, seed); + + for (i = 0; i < num; i++) { + string key(keygen_next_key(&keygen)); + RemoteDB::BulkRecord rec = { 0, key, value, INT64_MAX }; + + bulkrecs.push_back(rec); + + if (bulkrecs.size() >= batch) { + rdb->set_bulk_binary(bulkrecs); + bulkrecs.clear(); + } + } + if (bulkrecs.size()) + rdb->set_bulk_binary(bulkrecs); +} + static void putlist_test(void *db, const char *command, int num, int vsiz, int batch, unsigned int seed) { @@ -123,6 +149,37 @@ static void fwmkeys_test(void *db, int num, unsigned int seed) check_keys(&list, num, seed); } +static void check_bin_records(vector *bulkrecs, + struct keygen *keygen, int vsiz, int batch) +{ + int recnum; + + if (!debug) + return; + + recnum = bulkrecs->size(); + + if (recnum != batch) + die("Unexpected list size %d", recnum); + + vector::iterator it = bulkrecs->begin(); + vector::iterator end = bulkrecs->end(); + + while (it != end) { + const char *key = it->key.data(); + int keysiz = it->key.size(); + int valsiz = it->value.size(); + + if (strncmp(keygen_next_key(keygen), key, keysiz)) + die("Unexpected key"); + if (valsiz != vsiz) + die("Unexpected value size %d", valsiz); + + it++; + } +} + + static void check_records(map *recs, struct keygen *keygen, int vsiz, int batch) { @@ -153,6 +210,38 @@ static void check_records(map *recs, struct keygen *keygen, } } +static void getlist_bin_test(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed) +{ + RemoteDB *rdb = (RemoteDB *)db; + struct keygen keygen; + struct keygen keygen_for_check; + vector bulkrecs; + int i; + + keygen_init(&keygen, seed); + keygen_init(&keygen_for_check, seed); + + for (i = 0; i < num; i++) { + string key(keygen_next_key(&keygen)); + RemoteDB::BulkRecord rec = { 0, key, "", 0 }; + + bulkrecs.push_back(rec); + + if (bulkrecs.size() >= batch) { + rdb->get_bulk_binary(&bulkrecs); + check_bin_records(&bulkrecs, &keygen_for_check, vsiz, + bulkrecs.size()); + bulkrecs.clear(); + } + } + if (bulkrecs.size()) { + rdb->get_bulk_binary(&bulkrecs); + check_bin_records(&bulkrecs, &keygen_for_check, vsiz, + bulkrecs.size()); + } +} + static void getlist_test(void *db, const char *command, int num, int vsiz, int batch, unsigned int seed) { @@ -211,12 +300,43 @@ static void range_test(void *db, const char *command, int num, int vsiz, nrecs++; delete rec; } - if (num != nrecs) + if (debug && num != nrecs) die("Unexpected record num: %d", nrecs); delete cur; } +static void rangeout_test(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed) +{ + die("rangeout_test is not implemented"); +} + +static void outlist_bin_test(void *db, const char *command, int num, int batch, + unsigned int seed) +{ + RemoteDB *rdb = (RemoteDB *)db; + struct keygen keygen; + vector bulkrecs; + int i; + + keygen_init(&keygen, seed); + + for (i = 0; i < num; i++) { + string key(keygen_next_key(&keygen)); + RemoteDB::BulkRecord rec = { 0, key, "", 0 }; + + bulkrecs.push_back(rec); + + if (bulkrecs.size() >= batch) { + rdb->remove_bulk_binary(bulkrecs); + bulkrecs.clear(); + } + } + if (bulkrecs.size()) + rdb->remove_bulk_binary(bulkrecs); +} + static void outlist_test(void *db, const char *command, int num, int batch, unsigned int seed) { @@ -250,18 +370,27 @@ int main(int argc, char **argv) config.num = 5000000; config.vsiz = 100; config.batch = 1000; - config.thnum = 1; + config.producer_thnum = 1; + config.consumer_thnum = 1; config.debug = false; + config.verbose = 1; config.share = 0; config.ops.open_db = open_db; config.ops.close_db = close_db; config.ops.put_test = put_test; config.ops.get_test = get_test; - config.ops.putlist_test = putlist_test; config.ops.fwmkeys_test = fwmkeys_test; - config.ops.getlist_test = getlist_test; + config.ops.rangeout_test = rangeout_test; + if (0) { /* HTTP */ + config.ops.putlist_test = putlist_test; + config.ops.getlist_test = getlist_test; + config.ops.outlist_test = outlist_test; + } else { /* binary protocol */ + config.ops.putlist_test = putlist_bin_test; + config.ops.getlist_test = getlist_bin_test; + config.ops.outlist_test = outlist_bin_test; + } config.ops.range_test = range_test; - config.ops.outlist_test = outlist_test; parse_options(&config, argc, argv); debug = config.debug; diff --git a/testutil.c b/testutil.c index 50bdba6..84cc170 100644 --- a/testutil.c +++ b/testutil.c @@ -5,6 +5,7 @@ #include #include #include +#include #include "testutil.h" void die(const char *err, ...) @@ -129,12 +130,14 @@ static unsigned long long stopwatch_stop(unsigned long long start) static void fixup_config(struct benchmark_config *config) { - if (config->thnum < 1) - config->thnum = 1; + if (config->producer_thnum < 1) + config->producer_thnum = 1; + if (config->consumer_thnum < 1) + config->consumer_thnum = 1; if (config->share < 1) - config->share = config->thnum; + config->share = INT_MAX; if (config->num_works < 1) - config->num_works = config->thnum; + config->num_works = config->producer_thnum; } void parse_options(struct benchmark_config *config, int argc, char **argv) @@ -144,6 +147,7 @@ void parse_options(struct benchmark_config *config, int argc, char **argv) for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-command")) { config->producer = argv[++i]; + config->consumer = "nop"; } else if (!strcmp(argv[i], "-producer")) { config->producer = argv[++i]; } else if (!strcmp(argv[i], "-consumer")) { @@ -163,13 +167,20 @@ void parse_options(struct benchmark_config *config, int argc, char **argv) } else if (!strcmp(argv[i], "-batch")) { config->batch = atoi(argv[++i]); } else if (!strcmp(argv[i], "-thnum")) { - config->thnum = atoi(argv[++i]); + config->producer_thnum = atoi(argv[++i]); + config->consumer_thnum = config->producer_thnum; + } else if (!strcmp(argv[i], "-producer-thnum")) { + config->producer_thnum = atoi(argv[++i]); + } else if (!strcmp(argv[i], "-consumer-thnum")) { + config->consumer_thnum = atoi(argv[++i]); } else if (!strcmp(argv[i], "-work")) { config->num_works = atoi(argv[++i]); } else if (!strcmp(argv[i], "-key")) { keygen_set_generator(argv[++i]); } else if (!strcmp(argv[i], "-debug")) { config->debug = true; + } else if (!strcmp(argv[i], "-verbose")) { + config->verbose = atoi(argv[++i]); } else if (!strcmp(argv[i], "-share")) { config->share = atoi(argv[++i]); } else { @@ -181,13 +192,14 @@ void parse_options(struct benchmark_config *config, int argc, char **argv) } struct work { - struct work *next; unsigned int seed; - unsigned long long elapsed; + int progress; + unsigned long long start[2]; + unsigned long long elapsed[2]; }; struct work_queue { - struct work *head; + TCPTRLIST *list; pthread_mutex_t mutex; pthread_cond_t cond; bool open; @@ -211,19 +223,30 @@ static void work_queue_close(struct work_queue *queue) static void work_queue_init(struct work_queue *queue) { - queue->head = NULL; + queue->list = tcptrlistnew(); pthread_mutex_init(&queue->mutex, NULL); pthread_cond_init(&queue->cond, NULL); work_queue_open(queue); } +static void work_queue_destroy(struct work_queue *queue) +{ + pthread_mutex_lock(&queue->mutex); + if (tcptrlistnum(queue->list) > 0) + die("work queue is not empty"); + pthread_mutex_unlock(&queue->mutex); + + tcptrlistdel(queue->list); + pthread_mutex_destroy(&queue->mutex); + pthread_cond_destroy(&queue->cond); +} + static void work_queue_push(struct work_queue *queue, struct work *work) { pthread_mutex_lock(&queue->mutex); if (!queue->open) die("work queue is closed"); - work->next = queue->head; - queue->head = work; + tcptrlistunshift(queue->list, work); pthread_cond_signal(&queue->cond); pthread_mutex_unlock(&queue->mutex); } @@ -234,13 +257,14 @@ static struct work *work_queue_pop(struct work_queue *queue) pthread_mutex_lock(&queue->mutex); while (1) { - work = queue->head; - if (work) { - queue->head = work->next; - break; - } else if (!queue->open) { +#define WORK_QUEUE_FIFO +#ifdef WORK_QUEUE_FIFO + work = tcptrlistpop(queue->list); +#else /* LIFO */ + work = tcptrlistshift(queue->list); +#endif + if (work || !queue->open) break; - } pthread_cond_wait(&queue->cond, &queue->mutex); } pthread_mutex_unlock(&queue->mutex); @@ -262,7 +286,10 @@ static void handle_work(struct worker_info *data, struct work *work) const char *command = data->command; struct benchmark_config *config = data->config; struct benchmark_operations *bops = &config->ops; - unsigned long long start; + unsigned long start, elapsed; + + if (work->progress > 1) + die("something wrong happened"); start = stopwatch_start(); @@ -274,6 +301,9 @@ static void handle_work(struct worker_info *data, struct work *work) } else if (!strcmp(command, "range") || !strcmp(command, "range2")) { bops->range_test(data->db, command, config->num, config->vsiz, config->batch, work->seed); + } else if (!strcmp(command, "rangeout")) { + bops->rangeout_test(data->db, command, config->num, + config->vsiz, config->batch, work->seed); } else if (!strcmp(command, "getlist") || !strcmp(command, "getlist2")) { bops->getlist_test(data->db, command, config->num, config->vsiz, config->batch, work->seed); @@ -305,7 +335,11 @@ static void handle_work(struct worker_info *data, struct work *work) } else { die("Invalid command %s", command); } - work->elapsed += stopwatch_stop(start); + + elapsed = stopwatch_stop(start); + work->start[work->progress] = start; + work->elapsed[work->progress] = elapsed; + work->progress++; } static void *benchmark_thread(void *arg) @@ -322,10 +356,9 @@ static void *benchmark_thread(void *arg) } static struct worker_info *create_workers(struct benchmark_config *config, - const char *command, struct work_queue *in_queue, + int thnum, const char *command, struct work_queue *in_queue, struct work_queue *out_queue) { - int thnum = config->thnum; struct worker_info *data = xmalloc(sizeof(*data) * thnum); int i; @@ -346,21 +379,20 @@ static struct worker_info *create_workers(struct benchmark_config *config, return data; } -static void join_workers(struct benchmark_config *config, - struct worker_info *data) +static void join_workers(struct worker_info *data, int thnum) { int i; - for (i = 0; i < config->thnum; i++) + for (i = 0; i < thnum; i++) xpthread_join(data[i].tid); } -static void destroy_workers(struct worker_info *data) +static void destroy_workers(struct worker_info *data, int thnum) { struct benchmark_config *config = data[0].config; int i; - for (i = 0; i < config->thnum; i++) { + for (i = 0; i < thnum; i++) { if ((i % config->share) == i) config->ops.close_db(data[i].db); } @@ -379,57 +411,99 @@ static void destroy_workers(struct worker_info *data) (void) (&_max1 == &_max2); \ _max1 > _max2 ? _max1 : _max2; }) +static void collect_results(struct benchmark_config *config, + struct work_queue *queue, unsigned long long start, + unsigned long long elapsed) +{ + int i; + unsigned long long sum[2] = { 0, 0 }, min[2] = { ULONG_MAX, ULONG_MAX }; + unsigned long long max[2] = { 0, 0 }, avg[2]; + + for (i = 0; i < config->num_works; i++) { + struct work *work = work_queue_pop(queue); + + sum[0] += work->elapsed[0]; + sum[1] += work->elapsed[1]; + min[0] = _MIN(min[0], work->elapsed[0]); + min[1] = _MIN(min[1], work->elapsed[1]); + max[0] = _MAX(max[0], work->elapsed[0]); + max[1] = _MAX(max[1], work->elapsed[1]); + + if (config->verbose > 1) { + printf( + "%lld.%03lld %lld.%03lld %lld.%03lld %lld.%03lld\n", + (work->start[0] - start) / 1000000, + (work->start[0] - start) / 1000 % 1000, + work->elapsed[0] / 1000000, + work->elapsed[0] / 1000 % 1000, + (work->start[1] - start) / 1000000, + (work->start[1] - start) / 1000 % 1000, + work->elapsed[1] / 1000000, + work->elapsed[1] / 1000 % 1000); + } + free(work); + } + avg[0] = sum[0] / config->num_works; + avg[1] = sum[1] / config->num_works; + + if (config->verbose > 0) { + printf( + "# %lld.%03lld %lld.%03lld %lld.%03lld %lld.%03lld %lld.%03lld %lld.%03lld\n", + avg[0] / 1000000, avg[0] / 1000 % 1000, + min[0] / 1000000, min[0] / 1000 % 1000, + max[0] / 1000000, max[0] / 1000 % 1000, + avg[1] / 1000000, avg[1] / 1000 % 1000, + min[1] / 1000000, min[1] / 1000 % 1000, + max[1] / 1000000, max[1] / 1000 % 1000); + } +} + void benchmark(struct benchmark_config *config) { int i; - unsigned long long sum = 0, min = ULONG_MAX, max = 0, avg; struct worker_info *producers; struct worker_info *consumers; struct work_queue queue_to_producer; struct work_queue queue_to_consumer; struct work_queue trash_queue; + unsigned long long start, elapsed; work_queue_init(&queue_to_producer); work_queue_init(&queue_to_consumer); work_queue_init(&trash_queue); - producers = create_workers(config, config->producer, &queue_to_producer, + producers = create_workers(config, config->producer_thnum, + config->producer, &queue_to_producer, &queue_to_consumer); - consumers = create_workers(config, config->consumer, &queue_to_consumer, + consumers = create_workers(config, config->consumer_thnum, + config->consumer, &queue_to_consumer, &trash_queue); + start = stopwatch_start(); + for (i = 0; i < config->num_works; i++) { struct work *work = xmalloc(sizeof(*work)); + memset(work, 0, sizeof(*work)); work->seed = config->seed_offset + i; - work->elapsed = 0; work_queue_push(&queue_to_producer, work); } work_queue_close(&queue_to_producer); - join_workers(config, producers); + join_workers(producers, config->producer_thnum); work_queue_close(&queue_to_consumer); - join_workers(config, consumers); + join_workers(consumers, config->consumer_thnum); work_queue_close(&trash_queue); - for (i = 0; i < config->num_works; i++) { - struct work *work = work_queue_pop(&trash_queue); - unsigned long long elapsed = work->elapsed; + elapsed = stopwatch_stop(start); - sum += elapsed; - min = _MIN(min, elapsed); - max = _MAX(max, elapsed); - free(work); - } - avg = sum / config->num_works; + collect_results(config, &trash_queue, start, elapsed); - printf("# %lld.%03lld %lld.%03lld %lld.%03lld\n", - avg / 1000000, avg / 1000 % 1000, - min / 1000000, min / 1000 % 1000, - max / 1000000, max / 1000 % 1000); - fflush(stdout); + destroy_workers(consumers, config->consumer_thnum); + destroy_workers(producers, config->producer_thnum); - destroy_workers(consumers); - destroy_workers(producers); + work_queue_destroy(&queue_to_producer); + work_queue_destroy(&queue_to_consumer); + work_queue_destroy(&trash_queue); } diff --git a/testutil.h b/testutil.h index 84feac5..2db7810 100644 --- a/testutil.h +++ b/testutil.h @@ -45,6 +45,8 @@ struct benchmark_operations { int batch, unsigned int seed); void (*range_test)(void *db, const char *command, int num, int vsiz, int batch, unsigned int seed); + void (*rangeout_test)(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed); void (*outlist_test)(void *db, const char *command, int num, int batch, unsigned int seed); }; @@ -59,9 +61,11 @@ struct benchmark_config { int vsiz; unsigned int seed_offset; int batch; - int thnum; + int producer_thnum; + int consumer_thnum; int num_works; bool debug; + int verbose; int share; struct benchmark_operations ops; }; diff --git a/tokyocabinettest.c b/tokyocabinettest.c index 53e8934..b27785b 100644 --- a/tokyocabinettest.c +++ b/tokyocabinettest.c @@ -239,7 +239,7 @@ static void range1_test(void *db, int num, int vsiz, int batch, tclistdel(recs); num -= num_recs; } - if (num) + if (debug && num) die("Unexpected record num"); tclistdel(args); @@ -283,7 +283,7 @@ static void range2_test(void *db, int num, int vsiz, int batch, tclistdel(recs); num -= num_recs; } - if (num) + if (debug && num) die("Unexpected record num"); tclistdel(args); @@ -300,6 +300,52 @@ static void range_test(void *db, const char *command, int num, int vsiz, die("invalid range command"); } +static void rangeout_test(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed) +{ + TCADB *adb = db; + struct keygen keygen; + TCLIST *args = tclistnew(); + char start_key[KEYGEN_PREFIX_SIZE + 1]; + char max[100]; + char end_key[KEYGEN_PREFIX_SIZE + 1]; + char binc[2]; + + keygen_init(&keygen, seed); + + keygen_prefix(&keygen, start_key); + sprintf(max, "%d", batch); + keygen_prefix(&keygen, end_key); + end_key[KEYGEN_PREFIX_SIZE - 1] = '-' + 1; + sprintf(binc, "0"); + + tclistpush2(args, start_key); + tclistpush2(args, max); + tclistpush2(args, end_key); + tclistpush2(args, binc); + + while (1) { + TCLIST *recs; + + recs = do_tcadbmisc(adb, command, args); + if (tclistnum(recs) == 0) + break; + + if (debug) { + const char *num_recs = tclistval2(recs, 0); + num -= atoi(num_recs); + if (num != 0 && atoi(num_recs) != batch) + die("Unexpected number of records are deleted"); + } + + tclistdel(recs); + } + if (debug && num != 0) + die("Unexpected number of records are deleted"); + + tclistdel(args); +} + static void outlist_test(void *db, const char *command, int num, int batch, unsigned int seed) { @@ -331,8 +377,10 @@ struct benchmark_config config = { .num = 5000000, .vsiz = 100, .batch = 1000, - .thnum = 1, + .producer_thnum = 1, + .consumer_thnum = 1, .debug = false, + .verbose = 1, .share = 1, .ops = { .open_db = open_db, @@ -343,6 +391,7 @@ struct benchmark_config config = { .fwmkeys_test = fwmkeys_test, .getlist_test = getlist_test, .range_test = range_test, + .rangeout_test = rangeout_test, .outlist_test = outlist_test, }, }; diff --git a/tokyotyranttest.c b/tokyotyranttest.c index e0f5e3f..fb5be3e 100644 --- a/tokyotyranttest.c +++ b/tokyotyranttest.c @@ -244,7 +244,7 @@ static void range1_test(void *db, int num, int vsiz, int batch, tclistdel(recs); num -= num_recs; } - if (num) + if (debug && num) die("Unexpected record num"); tclistdel(args); @@ -288,7 +288,7 @@ static void range2_test(void *db, int num, int vsiz, int batch, tclistdel(recs); num -= num_recs; } - if (num) + if (debug && num) die("Unexpected record num"); tclistdel(args); @@ -305,6 +305,50 @@ static void range_test(void *db, const char *command, int num, int vsiz, die("invalid range command"); } +static void rangeout_test(void *db, const char *command, int num, int vsiz, + int batch, unsigned int seed) +{ + TCRDB *rdb = db; + struct keygen keygen; + TCLIST *args = tclistnew(); + char start_key[KEYGEN_PREFIX_SIZE + 1]; + char max[100]; + char end_key[KEYGEN_PREFIX_SIZE + 1]; + char binc[2]; + + keygen_init(&keygen, seed); + + keygen_prefix(&keygen, start_key); + sprintf(max, "%d", batch); + keygen_prefix(&keygen, end_key); + end_key[KEYGEN_PREFIX_SIZE - 1] = '-' + 1; + sprintf(binc, "0"); + + tclistpush2(args, start_key); + tclistpush2(args, max); + tclistpush2(args, end_key); + tclistpush2(args, binc); + + while (1) { + TCLIST *recs; + + recs = do_tcrdbmisc(rdb, command, args); + if (tclistnum(recs) == 0) + break; + if (debug) { + const char *num_recs = tclistval2(recs, 0); + num -= atoi(num_recs); + if (num != 0 && atoi(num_recs) != batch) + die("Unexpected number of records are deleted"); + } + tclistdel(recs); + } + if (debug && num != 0) + die("Unexpected number of records are deleted"); + + tclistdel(args); +} + static void outlist_test(void *db, const char *command, int num, int batch, unsigned int seed) { @@ -337,8 +381,10 @@ static struct benchmark_config config = { .num = 5000000, .vsiz = 100, .batch = 1000, - .thnum = 1, + .producer_thnum = 1, + .consumer_thnum = 1, .debug = false, + .verbose = 1, .share = 0, .ops = { .open_db = open_db, @@ -349,6 +395,7 @@ static struct benchmark_config config = { .fwmkeys_test = fwmkeys_test, .getlist_test = getlist_test, .range_test = range_test, + .rangeout_test = rangeout_test, .outlist_test = outlist_test, }, };