static size_t get_batch_requests_count(struct thread_task *const task) { static const size_t batch_requests_count = 10000; size_t requests_count = batch_requests_count; p_lock_lock(&task->lock); if (task->requests_count < batch_requests_count) { requests_count = task->requests_count; } task->requests_count -= requests_count; p_lock_unlock(&task->lock); return requests_count; }
static void simple_set(struct ybc *const cache, const size_t requests_count, const size_t items_count, const size_t max_item_size) { struct m_rand_state rand_state; uint64_t tmp; char *const buf = p_malloc(max_item_size); const struct ybc_key key = { .ptr = &tmp, .size = sizeof(tmp), }; struct ybc_value value = { .ptr = buf, .size = 0, .ttl = YBC_MAX_TTL, }; m_rand_init(&rand_state); for (size_t i = 0; i < requests_count; ++i) { tmp = m_rand_next(&rand_state) % items_count; value.size = m_rand_next(&rand_state) % (max_item_size + 1); m_memset(buf, (char)value.size, value.size); if (!ybc_item_set(cache, &key, &value)) { M_ERROR("Cannot store item in the cache"); } } p_free(buf); } static void simple_set_simple(struct ybc *const cache, const size_t requests_count, const size_t items_count, const size_t max_item_size) { struct m_rand_state rand_state; uint64_t tmp; char *const buf = p_malloc(max_item_size); const struct ybc_key key = { .ptr = &tmp, .size = sizeof(tmp), }; struct ybc_value value = { .ptr = buf, .size = 0, .ttl = YBC_MAX_TTL, }; m_rand_init(&rand_state); for (size_t i = 0; i < requests_count; ++i) { tmp = m_rand_next(&rand_state) % items_count; value.size = m_rand_next(&rand_state) % (max_item_size + 1); m_memset(buf, (char)value.size, value.size); if (!ybc_simple_set(cache, &key, &value)) { M_ERROR("Cannot store item in the cache"); } } p_free(buf); } static void simple_get_miss(struct ybc *const cache, const size_t requests_count, const size_t items_count) { char item_buf[ybc_item_get_size()]; struct ybc_item *const item = (struct ybc_item *)item_buf; struct m_rand_state rand_state; uint64_t tmp; const struct ybc_key key = { .ptr = &tmp, .size = sizeof(tmp), }; m_rand_init(&rand_state); for (size_t i = 0; i < requests_count; ++i) { tmp = m_rand_next(&rand_state) % items_count; if (ybc_item_get(cache, item, &key)) { M_ERROR("Unexpected item found"); } } } static void simple_get_hit(struct ybc *const cache, const size_t requests_count, const size_t items_count, const size_t max_item_size) { char item_buf[ybc_item_get_size()]; struct ybc_item *const item = (struct ybc_item *)item_buf; struct m_rand_state rand_state; uint64_t tmp; const struct ybc_key key = { .ptr = &tmp, .size = sizeof(tmp), }; struct ybc_value value; m_rand_init(&rand_state); for (size_t i = 0; i < requests_count; ++i) { tmp = m_rand_next(&rand_state) % items_count; if (ybc_item_get(cache, item, &key)) { /* Emulate access to the item */ ybc_item_get_value(item, &value); if (value.size > max_item_size) { M_ERROR("Unexpected value size"); } if (!m_memset_check(value.ptr, (char)value.size, value.size)) { fprintf(stderr, "i=%zu, requests_count=%zu, value.size=%zu\n", i, requests_count, value.size); M_ERROR("Unexpected value"); } ybc_item_release(item); } } } static void simple_get_simple_hit(struct ybc *const cache, const size_t requests_count, const size_t items_count, const size_t max_item_size) { struct m_rand_state rand_state; uint64_t tmp; const struct ybc_key key = { .ptr = &tmp, .size = sizeof(tmp), }; struct ybc_value value; value.size = max_item_size; value.ptr = p_malloc(value.size); m_rand_init(&rand_state); for (size_t i = 0; i < requests_count; ++i) { tmp = m_rand_next(&rand_state) % items_count; value.size = max_item_size; int rv = ybc_simple_get(cache, &key, &value); if (rv == 0) { continue; } assert(rv == 1); if (value.size > max_item_size) { M_ERROR("Unexpected value size"); } if (!m_memset_check(value.ptr, (char)value.size, value.size)) { fprintf(stderr, "i=%zu, requests_count=%zu, value.size=%zu\n", i, requests_count, value.size); M_ERROR("Unexpected value"); } } p_free((void *)value.ptr); } static void m_open(struct ybc *const cache, const int use_shm, const size_t items_count, const size_t hot_items_count, const size_t max_item_size, const int has_overwrite_protection) { char config_buf[ybc_config_get_size()]; struct ybc_config *const config = (struct ybc_config *)config_buf; const size_t data_file_size = max_item_size * items_count; const size_t hot_data_size = max_item_size * hot_items_count; ybc_config_init(config); if (use_shm) { ybc_config_set_data_file(config, "/dev/shm/ybc-perftest-cache.data"); ybc_config_set_index_file(config, "/dev/shm/ybc-perftest-cache.index"); } ybc_config_set_max_items_count(config, items_count); ybc_config_set_hot_items_count(config, hot_items_count); ybc_config_set_data_file_size(config, data_file_size); ybc_config_set_hot_data_size(config, hot_data_size); if (!has_overwrite_protection) { ybc_config_disable_overwrite_protection(config); } if (!ybc_open(cache, config, 1)) { M_ERROR("Cannot create a cache"); } ybc_config_destroy(config); if (use_shm) { ybc_clear(cache); } } static void m_close(struct ybc *const cache, const int use_shm) { ybc_close(cache); if (!use_shm) { return; } char config_buf[ybc_config_get_size()]; struct ybc_config *const config = (struct ybc_config *)config_buf; ybc_config_init(config); ybc_config_set_data_file(config, "/dev/shm/ybc-perftest-cache.data"); ybc_config_set_index_file(config, "/dev/shm/ybc-perftest-cache.index"); ybc_remove(config); ybc_config_destroy(config); } static void measure_simple_ops(struct ybc *const cache, const int use_shm, const size_t requests_count, const size_t items_count, const size_t hot_items_count, const size_t max_item_size, const int has_overwrite_protection) { double start_time, end_time; double qps; m_open(cache, use_shm, items_count, hot_items_count, max_item_size, has_overwrite_protection); printf("simple_ops(requests=%zu, items=%zu, " "hot_items=%zu, max_item_size=%zu, has_overwrite_protection=%d, use_shm=%d)\n", requests_count, items_count, hot_items_count, max_item_size, has_overwrite_protection, use_shm); start_time = p_get_current_time(); simple_get_miss(cache, requests_count, items_count); end_time = p_get_current_time(); qps = requests_count / (end_time - start_time) * 1000; printf(" get_miss : %.02f qps\n", qps); start_time = p_get_current_time(); simple_set(cache, requests_count, items_count, max_item_size); end_time = p_get_current_time(); qps = requests_count / (end_time - start_time) * 1000; printf(" set : %.02f qps\n", qps); const size_t get_items_count = hot_items_count ? hot_items_count : items_count; if (has_overwrite_protection) { start_time = p_get_current_time(); simple_get_hit(cache, requests_count, get_items_count, max_item_size); end_time = p_get_current_time(); qps = requests_count / (end_time - start_time) * 1000; printf(" get_hit : %.02f qps\n", qps); } ybc_clear(cache); start_time = p_get_current_time(); simple_set_simple(cache, requests_count, items_count, max_item_size); end_time = p_get_current_time(); qps = requests_count / (end_time - start_time) * 1000; printf(" set_simple : %.02f qps\n", qps); start_time = p_get_current_time(); simple_get_simple_hit(cache, requests_count, get_items_count, max_item_size); end_time = p_get_current_time(); qps = requests_count / (end_time - start_time) * 1000; printf(" get_simple_hit : %.02f qps\n", qps); m_close(cache, use_shm); } struct thread_task { struct p_lock lock; struct ybc *cache; size_t requests_count; size_t items_count; size_t get_items_count; size_t max_item_size; }; static size_t get_batch_requests_count(struct thread_task *const task) { static const size_t batch_requests_count = 10000; size_t requests_count = batch_requests_count; p_lock_lock(&task->lock); if (task->requests_count < batch_requests_count) { requests_count = task->requests_count; } task->requests_count -= requests_count; p_lock_unlock(&task->lock); return requests_count; } static void thread_func_set(void *const ctx) { struct thread_task *const task = ctx; for (;;) { const size_t requests_count = get_batch_requests_count(task); if (requests_count == 0) { break; } simple_set(task->cache, requests_count, task->items_count, task->max_item_size); } } static void thread_func_get_miss(void *const ctx) { struct thread_task *const task = ctx; for (;;) { const size_t requests_count = get_batch_requests_count(task); if (requests_count == 0) { break; } simple_get_miss(task->cache, requests_count, task->get_items_count); } } static void thread_func_get_hit(void *const ctx) { struct thread_task *const task = ctx; for (;;) { const size_t requests_count = get_batch_requests_count(task); if (requests_count == 0) { break; } simple_get_hit(task->cache, requests_count, task->get_items_count, task->max_item_size); } } static void thread_func_set_get(void *const ctx) { struct thread_task *const task = ctx; for (;;) { const size_t requests_count = get_batch_requests_count(task); if (requests_count == 0) { break; } const size_t set_requests_count = (size_t)(requests_count * 0.1); const size_t get_requests_count = requests_count - set_requests_count; simple_set(task->cache, set_requests_count, task->items_count, task->max_item_size); simple_get_hit(task->cache, get_requests_count, task->get_items_count, task->max_item_size); } } static void thread_func_set_simple(void *const ctx) { struct thread_task *const task = ctx; for (;;) { const size_t requests_count = get_batch_requests_count(task); if (requests_count == 0) { break; } simple_set_simple(task->cache, requests_count, task->items_count, task->max_item_size); } } static void thread_func_get_simple_hit(void *const ctx) { struct thread_task *const task = ctx; for (;;) { const size_t requests_count = get_batch_requests_count(task); if (requests_count == 0) { break; } simple_get_simple_hit(task->cache, requests_count, task->get_items_count, task->max_item_size); } } static double measure_qps(struct thread_task *const task, const p_thread_func thread_func, const size_t threads_count, const size_t requests_count) { struct p_thread threads[threads_count]; task->requests_count = requests_count; double start_time = p_get_current_time(); for (size_t i = 0; i < threads_count; ++i) { p_thread_init_and_start(&threads[i], thread_func, task); } for (size_t i = 0; i < threads_count; ++i) { p_thread_join_and_destroy(&threads[i]); } double end_time = p_get_current_time(); return requests_count / (end_time - start_time) * 1000; } static void measure_multithreaded_ops(struct ybc *const cache, const int use_shm, const size_t threads_count, const size_t requests_count, const size_t items_count, const size_t hot_items_count, const size_t max_item_size, const int has_overwrite_protection) { double qps; m_open(cache, use_shm, items_count, hot_items_count, max_item_size, has_overwrite_protection); struct thread_task task = { .cache = cache, .items_count = items_count, .get_items_count = hot_items_count ? hot_items_count : items_count, .max_item_size = max_item_size, }; p_lock_init(&task.lock); printf("multithreaded_ops(requests=%zu, items=%zu, hot_items=%zu, " "max_item_size=%zu, threads=%zu, has_overwrite_protection=%d, use_shm=%d)\n", requests_count, items_count, hot_items_count, max_item_size, threads_count, has_overwrite_protection, use_shm); qps = measure_qps(&task, thread_func_get_miss, threads_count, requests_count); printf(" get_miss : %.2f qps\n", qps); qps = measure_qps(&task, thread_func_set, threads_count, requests_count); printf(" set : %.2f qps\n", qps); if (has_overwrite_protection) { qps = measure_qps(&task, thread_func_get_hit, threads_count, requests_count); printf(" get_hit : %.2f qps\n", qps); qps = measure_qps(&task, thread_func_set_get, threads_count, requests_count); printf(" get_set : %.2f qps\n", qps); } ybc_clear(cache); qps = measure_qps(&task, thread_func_set_simple, threads_count, requests_count); printf(" set_simple : %.2f qps\n", qps); qps = measure_qps(&task, thread_func_get_simple_hit, threads_count, requests_count); printf(" get_simple_hit : %.2f qps\n", qps); p_lock_destroy(&task.lock); m_close(cache, use_shm); } int main(void) { char cache_buf[ybc_get_size()]; struct ybc *const cache = (struct ybc *)cache_buf; const size_t requests_count = 4 * 1000 * 1000; const size_t items_count = 200 * 1000; for (size_t max_item_size = 8; max_item_size <= 4096; max_item_size *= 2) { measure_simple_ops(cache, 0, requests_count, items_count, 0, max_item_size, 0); measure_simple_ops(cache, 0, requests_count, items_count, 0, max_item_size, 1); measure_simple_ops(cache, 1, requests_count, items_count, 0, max_item_size, 0); measure_simple_ops(cache, 1, requests_count, items_count, 0, max_item_size, 1); for (size_t hot_items_count = 1000; hot_items_count <= items_count; hot_items_count *= 10) { measure_simple_ops(cache, 0, requests_count, items_count, hot_items_count, max_item_size, 0); measure_simple_ops(cache, 0, requests_count, items_count, hot_items_count, max_item_size, 1); measure_simple_ops(cache, 1, requests_count, items_count, hot_items_count, max_item_size, 0); measure_simple_ops(cache, 1, requests_count, items_count, hot_items_count, max_item_size, 1); } for (size_t threads_count = 1; threads_count <= 16; threads_count *= 2) { measure_multithreaded_ops(cache, 0, threads_count, requests_count, items_count, 10 * 1000, max_item_size, 0); measure_multithreaded_ops(cache, 0, threads_count, requests_count, items_count, 10 * 1000, max_item_size, 1); measure_multithreaded_ops(cache, 1, threads_count, requests_count, items_count, 10 * 1000, max_item_size, 0); measure_multithreaded_ops(cache, 1, threads_count, requests_count, items_count, 10 * 1000, max_item_size, 1); } } printf("All performance tests done\n"); return 0; }