From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-lf1-f47.google.com (mail-lf1-f47.google.com [209.85.167.47]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 026744765E2 for ; Wed, 23 Dec 2020 16:15:06 +0300 (MSK) Received: by mail-lf1-f47.google.com with SMTP id o17so40031283lfg.4 for ; Wed, 23 Dec 2020 05:15:06 -0800 (PST) From: mechanik20051988 Date: Wed, 23 Dec 2020 16:14:48 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH v2 2/3] test: add small allocator performance test List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: v.shpilevoy@tarantool.org Cc: mechanik20051988 , tarantool-patches@dev.tarantool.org From: mechanik20051988 --- CMakeLists.txt | 1 + perf/.gitignore | 1 + perf/CMakeLists.txt | 7 + perf/small_alloc_perf.c | 375 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 384 insertions(+) create mode 100644 perf/.gitignore create mode 100644 perf/CMakeLists.txt create mode 100644 perf/small_alloc_perf.c diff --git a/CMakeLists.txt b/CMakeLists.txt index d797128..b0dec0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,7 @@ target_link_libraries(${PROJECT_NAME} m) enable_testing() add_subdirectory(test) +add_subdirectory(perf) if(DEFINED SMALL_EMBEDDED) # Don't build shared library and skip INSTALL() targets if this diff --git a/perf/.gitignore b/perf/.gitignore new file mode 100644 index 0000000..9ed3b07 --- /dev/null +++ b/perf/.gitignore @@ -0,0 +1 @@ +*.test diff --git a/perf/CMakeLists.txt b/perf/CMakeLists.txt new file mode 100644 index 0000000..1a9d933 --- /dev/null +++ b/perf/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(small_alloc_perf.test small_alloc_perf.c) +if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(LIBRT "rt") +endif () + +target_link_libraries(small_alloc_perf.test small ${LIBRT}) +include_directories("${PROJECT_SOURCE_DIR}") diff --git a/perf/small_alloc_perf.c b/perf/small_alloc_perf.c new file mode 100644 index 0000000..3b9b443 --- /dev/null +++ b/perf/small_alloc_perf.c @@ -0,0 +1,375 @@ +#include +#include +#include +#include +#include +#include +#include "../test/unit.h" + +enum { + OBJSIZE_MIN = 3 * sizeof(int), + OBJECTS_MAX = 1000 +}; + +const size_t SLAB_SIZE_MIN = 4 * 1024 * 1024; +const size_t SLAB_SIZE_MAX = 16 * 1024 * 1024; +static const unsigned long long NANOSEC_PER_SEC = 1000000000; +static const unsigned long long NANOSEC_PER_MSEC = 1000000; +#define SZR(arr) sizeof(arr) / sizeof(arr[0]) + +float slab_alloc_factor[] = {1.01, 1.03, 1.05, 1.1, 1.3, 1.5}; + +struct slab_arena arena; +struct slab_cache cache; +struct small_alloc alloc; +struct quota quota; +/** Streak type - allocating or freeing */ +bool allocating = true; +/** Enable human output */ +bool human = false; +/** Keep global to easily inspect the core. */ +long seed; +char json_output[100000]; +size_t length = sizeof(json_output); +size_t pos = 0; + +static int *ptrs[OBJECTS_MAX]; + +static inline +long long int timediff(struct timespec *tm1, struct timespec *tm2) +{ + return NANOSEC_PER_SEC * (tm2->tv_sec - tm1->tv_sec) + + (tm2->tv_nsec - tm1->tv_nsec); +} + +static inline void +free_checked(int *ptr) +{ + int pos = ptr[0]; + smfree_delayed(&alloc, ptrs[pos], ptrs[pos][1]); + ptrs[pos] = NULL; +} + +static float +calculate_pow_factor(int size_max, int pow_max, int start) +{ + return exp(log((double)size_max / start) / pow_max); +} + +static inline void * +alloc_checked(int pos, int size_min, int size_max, int rnd, double pow_factor) +{ + int size; + if (ptrs[pos]) + free_checked(ptrs[pos]); + + if (!allocating) + return NULL; + + if (rnd) { + size = size_min + (rand() % (size_max - size_min)); + } else { + size = floor(256 * pow(pow_factor, pos)); + } + ptrs[pos] = smalloc(&alloc, size); + if (ptrs[pos] == NULL) + return NULL; + ptrs[pos][0] = pos; + ptrs[pos][1] = size; + return ptrs[pos]; +} + +static int +small_is_unused_cb(const struct mempool_stats *stats, void *arg) +{ + unsigned long *slab_total = arg; + *slab_total += stats->slabsize * stats->slabcount; + return 0; +} + +static bool +small_is_unused(void) +{ + struct small_stats totals; + unsigned long slab_total = 0; + small_stats(&alloc, &totals, small_is_unused_cb, &slab_total); + if (totals.used > 0) + return false; + if (slab_cache_used(&cache) > slab_total) + return false; + return true; +} + +static void +small_alloc_test(int size_min, int size_max, int iterations_max, + int rnd, int cnt) +{ + double pow_factor = calculate_pow_factor(size_max, cnt, 256); + for (int i = 0; i <= iterations_max; i++) { + int mode = i % 3; + switch (mode) { + case 1: + small_alloc_setopt(&alloc, + SMALL_DELAYED_FREE_MODE, false); + break; + case 2: + small_alloc_setopt(&alloc, + SMALL_DELAYED_FREE_MODE, true); + break; + default: + break; + } + for (int j = 0; j < cnt; ++j) + alloc_checked(j, size_min, size_max, rnd, pow_factor); + allocating = !allocating; + } + + small_alloc_setopt(&alloc, SMALL_DELAYED_FREE_MODE, false); + + for (int pos = 0; pos < cnt; pos++) { + if (ptrs[pos] != NULL) + free_checked(ptrs[pos]); + } + + /* Trigger garbage collection. */ + allocating = true; + for (int i = 0; i < iterations_max; i++) { + if (small_is_unused()) + break; + void *p = alloc_checked(0, size_min, size_max, rnd, pow_factor); + if (p != NULL) + free_checked(p); + } +} + +static void +print_json_test_header(const char *type) +{ + size_t x = snprintf (json_output + pos, length, + " \"%s\": {\n", type); + length -=x; + pos +=x; + x = snprintf (json_output + pos, length, + " \"alloc factor\": {\n"); + length -=x; + pos +=x; + for (unsigned int i = 0; i < SZR(slab_alloc_factor); i++) { + size_t x = snprintf (json_output + pos, length, + " \"%.4f\"\n", slab_alloc_factor[i]); + length -=x; + pos +=x; + } + x = snprintf (json_output + pos, length, " },\n"); + length -=x; + pos +=x; + x = snprintf (json_output + pos, length, + " \"time, s\": {\n"); + length -=x; + pos +=x; +} + +static void +print_json_test_finish(const char * finish) +{ + size_t x = snprintf (json_output + pos, length, " }\n"); + length -=x; + pos +=x; + x = snprintf (json_output + pos, length, " }%s\n", finish); + length -=x; + pos +=x; +} + +static void +print_json_test_result(double time) +{ + size_t x = snprintf (json_output + pos, length, + " \"%.3f\"\n", time); + length -=x; + pos +=x; +} + +static void +small_alloc_basic(unsigned int slab_size) +{ + struct timespec tm1, tm2; + if(human) { + fprintf(stderr, "| SMALL RANDOM " + "ALLOCATION RESULT TABLE |\n"); + fprintf(stderr, "|___________________________________" + "_________________________________|\n"); + fprintf(stderr, "| alloc_factor " + " | time, ms |\n"); + fprintf(stderr, "|__________________________________|" + "_________________________________|\n"); + } else { + print_json_test_header("random"); + } + quota_init("a, UINT_MAX); + slab_arena_create(&arena, "a, 0, slab_size, MAP_PRIVATE); + slab_cache_create(&cache, &arena); + for (unsigned int i = 0; i < SZR(slab_alloc_factor); i++) { + small_alloc_create(&alloc, &cache, + OBJSIZE_MIN, slab_alloc_factor[i]); + int size_min = OBJSIZE_MIN; + int size_max = (int)alloc.objsize_max - 1; + fail_unless(clock_gettime (CLOCK_MONOTONIC, &tm1) == 0); + small_alloc_test(size_min, size_max, 300, 1, OBJECTS_MAX); + fail_unless(clock_gettime (CLOCK_MONOTONIC, &tm2) == 0); + if (human) { + fprintf(stderr, "| %.4f |" + " %6llu |\n", + slab_alloc_factor[i], + timediff(&tm1, &tm2) / NANOSEC_PER_MSEC); + } else { + print_json_test_result(timediff(&tm1, &tm2) / + NANOSEC_PER_MSEC); + } + small_alloc_destroy(&alloc); + } + slab_cache_destroy(&cache); + slab_arena_destroy(&arena); + quota_init("a, UINT_MAX); + slab_arena_create(&arena, "a, 0, slab_size, MAP_PRIVATE); + slab_cache_create(&cache, &arena); + if (human) { + fprintf(stderr, "|__________________________________|" + "_________________________________|\n"); + fprintf(stderr, "| SMALL EXP GROW " + "ALLOCATION RESULT TABLE |\n"); + fprintf(stderr, "|___________________________________" + "_________________________________|\n"); + fprintf(stderr, "| alloc_factor " + " | time, ms |\n"); + fprintf(stderr, "|__________________________________|" + "_________________________________|\n"); + } else { + print_json_test_finish(","); + print_json_test_header("exponent"); + } + for (unsigned int i = 0; i < SZR(slab_alloc_factor); i++) { + small_alloc_create(&alloc, &cache, + OBJSIZE_MIN, slab_alloc_factor[i]); + int size_min = OBJSIZE_MIN; + int size_max = (int)alloc.objsize_max - 1; + fail_unless(clock_gettime (CLOCK_MONOTONIC, &tm1) == 0); + small_alloc_test(size_min, size_max, 1000, 0, OBJECTS_MAX); + fail_unless(clock_gettime (CLOCK_MONOTONIC, &tm2) == 0); + if (human) { + fprintf(stderr, "| %.4f |" + " %6llu |\n", + slab_alloc_factor[i], + timediff(&tm1, &tm2) / NANOSEC_PER_MSEC); + } else { + print_json_test_result(timediff(&tm1, &tm2) / + NANOSEC_PER_MSEC); + } + small_alloc_destroy(&alloc); + } + if (human) { + fprintf(stderr, "|___________________________________" + "_________________________________|\n"); + } else { + print_json_test_finish(","); + } + slab_cache_destroy(&cache); + slab_arena_destroy(&arena); +} + +static void +small_alloc_large() +{ + struct timespec tm1, tm2; + size_t large_size_min = mempool_objsize_max(cache.arena->slab_size); + size_t large_size_max = 2 * cache.arena->slab_size; + if (human) { + fprintf(stderr, "| LARGE RANDOM " + "ALLOCATION RESULT TABLE |\n"); + fprintf(stderr, "|___________________________________" + "_________________________________|\n"); + fprintf(stderr, "| alloc_factor " + " | time, ms |\n"); + fprintf(stderr, "|__________________________________|" + "_________________________________|\n"); + } else { + print_json_test_header("large"); + } + for (unsigned int i = 0; i < SZR(slab_alloc_factor); i++) { + small_alloc_create(&alloc, &cache, OBJSIZE_MIN, + slab_alloc_factor[i]); + fail_unless(clock_gettime (CLOCK_MONOTONIC, &tm1) == 0); + small_alloc_test(large_size_min, large_size_max, 200, 1, 25); + fail_unless(clock_gettime (CLOCK_MONOTONIC, &tm2) == 0); + if (human) { + fprintf(stderr, "| %.4f |" + " %6llu |\n", + slab_alloc_factor[i], + timediff(&tm1, &tm2) / NANOSEC_PER_MSEC); + } else { + print_json_test_result(timediff(&tm1, &tm2) / + NANOSEC_PER_MSEC); + } + small_alloc_destroy(&alloc); + } + if (human) { + fprintf(stderr, "|___________________________________" + "_________________________________|\n"); + } else { + print_json_test_finish(""); + } +} + +int main(int argc, char* argv[]) +{ + size_t x; + seed = time(0); + srand(seed); + + if (argc == 2 && !strcmp(argv[1], "-h")) //human clear output + human = true; + + if (!human) { + x = snprintf (json_output + pos, length, "{\n"); + length -=x; + pos +=x; + } + for (unsigned int slab_size = SLAB_SIZE_MIN; slab_size <= SLAB_SIZE_MAX; + slab_size *= 2) { + if(human) { + fprintf(stderr, "_____________________________________" + "_________________________________\n"); + fprintf(stderr, "| PERFORMANCE TEST WITH SLABSIZE " + "%8u BYTES |\n", slab_size); + fprintf(stderr, "|___________________________________" + "_________________________________|\n"); + } else { + size_t x = snprintf (json_output + pos, length, + " \"test\": {\n"); + length -=x; + pos +=x; + x = snprintf (json_output + pos, length, + " \"slab size, bytes\": \"%u\",\n", + slab_size); + length -=x; + pos +=x; + } + small_alloc_basic(slab_size); + quota_init("a, UINT_MAX); + slab_arena_create(&arena, "a, 0, slab_size, MAP_PRIVATE); + slab_cache_create(&cache, &arena); + small_alloc_large(); + slab_cache_destroy(&cache); + slab_arena_destroy(&arena); + if (!human) { + x = snprintf (json_output + pos, length, + " }%s\n", + (slab_size == SLAB_SIZE_MAX ? "" : ",")); + length -=x; + pos +=x; + } + } + if (!human) { + x = snprintf (json_output + pos, length, "}\n"); + fprintf(stderr, "%s\n", json_output); + } + return EXIT_SUCCESS; +} -- 2.20.1