Tarantool development patches archive
 help / color / mirror / Atom feed
From: mechanik20051988 <mechanik20.05.1988@gmail.com>
To: v.shpilevoy@tarantool.org
Cc: mechanik20051988 <mechanik20.05.1988@gmail.com>,
	tarantool-patches@dev.tarantool.org
Subject: [Tarantool-patches] [PATCH v2 2/3] test: add small allocator performance test
Date: Wed, 23 Dec 2020 16:14:48 +0300	[thread overview]
Message-ID: <a178074468fec6461d87772794748b4b004f1a26.1608715671.git.mechanik20051988@gmail.com> (raw)
In-Reply-To: <cover.1608715671.git.mechanik20051988@gmail.com>

From: mechanik20051988 <mechanik20.05.1988@gmail.com>

---
 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 <small/small.h>
+#include <small/quota.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <math.h>
+#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(&quota, UINT_MAX);
+	slab_arena_create(&arena, &quota, 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(&quota, UINT_MAX);
+	slab_arena_create(&arena, &quota, 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(&quota, UINT_MAX);
+		slab_arena_create(&arena, &quota, 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

  parent reply	other threads:[~2020-12-23 13:15 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-23 13:14 [Tarantool-patches] [PATCH v2 0/3] change small allocator behavior mechanik20051988
2020-12-23 13:14 ` [Tarantool-patches] [PATCH] memtx: " mechanik20051988
2020-12-24 15:13   ` Vladislav Shpilevoy
     [not found]     ` <0076A088-8CBC-4238-9EEB-0C73EC516098@hxcore.ol>
2020-12-25  7:42       ` [Tarantool-patches] FW: " Evgeny Mekhanik
2020-12-28 12:10         ` Vladislav Shpilevoy
2020-12-23 13:14 ` [Tarantool-patches] [PATCH v2 1/3] small: implement new size class evaluation mechanik20051988
2020-12-24 15:13   ` Vladislav Shpilevoy
     [not found]     ` <A90D94B7-298A-4D1B-8134-6EE2ED45D615@hxcore.ol>
2020-12-25  7:48       ` [Tarantool-patches] FW: " Evgeny Mekhanik
2020-12-28 12:10     ` [Tarantool-patches] " Vladislav Shpilevoy
2020-12-23 13:14 ` mechanik20051988 [this message]
2020-12-23 13:14 ` [Tarantool-patches] [PATCH v2 3/3] small: changed small allocator pool management mechanik20051988
2020-12-24 15:13   ` Vladislav Shpilevoy
     [not found]     ` <27E47303-4307-4713-BB8A-2427FED09DDE@hxcore.ol>
2020-12-25  7:52       ` [Tarantool-patches] FW: " Evgeny Mekhanik
2020-12-25  7:56       ` Evgeny Mekhanik

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a178074468fec6461d87772794748b4b004f1a26.1608715671.git.mechanik20051988@gmail.com \
    --to=mechanik20.05.1988@gmail.com \
    --cc=tarantool-patches@dev.tarantool.org \
    --cc=v.shpilevoy@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH v2 2/3] test: add small allocator performance test' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox