From d458dd2996632cce68bc659ecdeedb989c5681b3 Mon Sep 17 00:00:00 2001
From: Tomas Vondra <tomas.vondra@postgresql.org>
Date: Sat, 31 Jul 2021 22:55:36 +0200
Subject: [PATCH 1/3] slab bench

---
 contrib/slab_bench/.gitignore          |   4 +
 contrib/slab_bench/Makefile            |  21 ++
 contrib/slab_bench/bench.sql           |  30 ++
 contrib/slab_bench/slab_bench--1.0.sql |  16 +
 contrib/slab_bench/slab_bench.c        | 405 +++++++++++++++++++++++++
 contrib/slab_bench/slab_bench.control  |   4 +
 6 files changed, 480 insertions(+)
 create mode 100644 contrib/slab_bench/.gitignore
 create mode 100644 contrib/slab_bench/Makefile
 create mode 100644 contrib/slab_bench/bench.sql
 create mode 100644 contrib/slab_bench/slab_bench--1.0.sql
 create mode 100644 contrib/slab_bench/slab_bench.c
 create mode 100644 contrib/slab_bench/slab_bench.control

diff --git a/contrib/slab_bench/.gitignore b/contrib/slab_bench/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/slab_bench/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/slab_bench/Makefile b/contrib/slab_bench/Makefile
new file mode 100644
index 0000000000..c423bbd4ca
--- /dev/null
+++ b/contrib/slab_bench/Makefile
@@ -0,0 +1,21 @@
+# contrib/slab_bench/Makefile
+
+MODULE_big = slab_bench
+OBJS = slab_bench.o
+
+EXTENSION = slab_bench
+DATA = slab_bench--1.0.sql
+PGFILEDESC = "slab_bench - slab context benchmarking functions"
+
+REGRESS = slab_bench
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/slab_bench
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/slab_bench/bench.sql b/contrib/slab_bench/bench.sql
new file mode 100644
index 0000000000..bcf6e05de0
--- /dev/null
+++ b/contrib/slab_bench/bench.sql
@@ -0,0 +1,30 @@
+CREATE EXTENSION slab_bench;
+
+\o fifo-no-loops.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_fifo(1000000, block_size, chunk_size, 0, 0, 0) x;
+
+\o lifo-no-loops.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_lifo(1000000, block_size, chunk_size, 0, 0, 0) x;
+
+\o random-no-loops.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_random(1000000, block_size, chunk_size, 0, 0, 0) x;
+
+
+\o fifo-increase.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_fifo(1000000, block_size, chunk_size, 100, 10000, 15000) x;
+
+\o lifo-increase.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_lifo(1000000, block_size, chunk_size, 100, 10000, 15000) x;
+
+\o random-increase.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_random(1000000, block_size, chunk_size, 100, 10000, 15000) x;
+
+
+\o fifo-decrease.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_fifo(1000000, block_size, chunk_size, 100, 10000, 5000) x;
+
+\o lifo-decrease.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_lifo(1000000, block_size, chunk_size, 100, 10000, 5000) x;
+
+\o random-decrease.data
+select run, block_size, chunk_size, 1000000 * chunk_size, x.* from generate_series(1,5) r(run), generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral slab_bench_random(1000000, block_size, chunk_size, 100, 10000, 5000) x;
diff --git a/contrib/slab_bench/slab_bench--1.0.sql b/contrib/slab_bench/slab_bench--1.0.sql
new file mode 100644
index 0000000000..0a23134d74
--- /dev/null
+++ b/contrib/slab_bench/slab_bench--1.0.sql
@@ -0,0 +1,16 @@
+/* slab_bench--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION slab_bench" to load this file. \quit
+
+CREATE FUNCTION slab_bench_random(nallocs bigint, block_size bigint, alloc_size bigint, loops int, free_cnt int, alloc_cnt int, out mem_allocated bigint, out alloc_ms bigint, out free_ms bigint)
+AS 'MODULE_PATHNAME', 'slab_bench_random'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION slab_bench_fifo(nallocs bigint, block_size bigint, alloc_size bigint, loops int, free_cnt int, alloc_cnt int, out mem_allocated bigint, out alloc_ms bigint, out free_ms bigint)
+AS 'MODULE_PATHNAME', 'slab_bench_fifo'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION slab_bench_lifo(nallocs bigint, block_size bigint, alloc_size bigint, loops int, free_cnt int, alloc_cnt int, out mem_allocated bigint, out alloc_ms bigint, out free_ms bigint)
+AS 'MODULE_PATHNAME', 'slab_bench_lifo'
+LANGUAGE C VOLATILE STRICT;
diff --git a/contrib/slab_bench/slab_bench.c b/contrib/slab_bench/slab_bench.c
new file mode 100644
index 0000000000..d2249ce4a2
--- /dev/null
+++ b/contrib/slab_bench/slab_bench.c
@@ -0,0 +1,405 @@
+/*-------------------------------------------------------------------------
+ *
+ * slab_bench.c
+ *
+ * helper functions to benchmark slab context with different workloads
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/time.h>
+
+#include "funcapi.h"
+#include "miscadmin.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(slab_bench_random);
+PG_FUNCTION_INFO_V1(slab_bench_fifo);
+PG_FUNCTION_INFO_V1(slab_bench_lifo);
+
+typedef struct Chunk {
+	int		random;
+	void   *ptr;
+} Chunk;
+
+static int
+chunk_index_cmp(const void *a, const void *b)
+{
+	Chunk *ca = (Chunk *) a;
+	Chunk *cb = (Chunk *) b;
+
+	if (ca->random < cb->random)
+		return -1;
+	else if (ca->random > cb->random)
+		return 1;
+
+	return 0;
+}
+
+Datum
+slab_bench_random(PG_FUNCTION_ARGS)
+{
+	MemoryContext	cxt,
+					oldcxt;
+	Chunk		   *chunks;
+	int64			i, j;
+	int64			nallocs = PG_GETARG_INT64(0);
+	int64			blockSize = PG_GETARG_INT64(1);
+	int64			chunkSize = PG_GETARG_INT64(2);
+
+	int				nloops = PG_GETARG_INT32(3);
+	int				free_cnt = PG_GETARG_INT32(4);
+	int				alloc_cnt = PG_GETARG_INT32(5);
+
+	struct timeval	start_time,
+					end_time;
+	int64			alloc_time = 0,
+					free_time = 0;
+	int64			mem_allocated;
+
+	TupleDesc		tupdesc;
+	Datum			result;
+	HeapTuple		tuple;
+	Datum			values[9];
+	bool			nulls[9];
+
+	int				maxchunks;
+
+	maxchunks = nallocs + nloops * Max(0, alloc_cnt - free_cnt);
+
+	cxt = SlabContextCreate(CurrentMemoryContext, "slab_bench", blockSize, chunkSize);
+
+	chunks = (Chunk *) palloc(maxchunks * sizeof(Chunk));
+
+	/* allocate the chunks in random order */
+	oldcxt = MemoryContextSwitchTo(cxt);
+
+	gettimeofday(&start_time, NULL);
+
+	for (i = 0; i < nallocs; i++)
+		chunks[i].ptr = palloc(chunkSize);
+
+	gettimeofday(&end_time, NULL);
+
+	alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+				  (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+	MemoryContextSwitchTo(oldcxt);
+
+	mem_allocated = MemoryContextMemAllocated(cxt, true);
+
+	/* do the requested number of free/alloc loops */
+	for (j = 0; j < nloops; j++)
+	{
+		/* randomize the indexes */
+		for (i = 0; i < nallocs; i++)
+			chunks[i].random = random();
+
+		qsort(chunks, nallocs, sizeof(Chunk), chunk_index_cmp);
+
+		oldcxt = MemoryContextSwitchTo(cxt);
+
+		gettimeofday(&start_time, NULL);
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < free_cnt; i++)
+			pfree(chunks[i].ptr);
+
+		gettimeofday(&end_time, NULL);
+
+		nallocs -= free_cnt;
+
+		free_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+					 (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+		memmove(chunks, &chunks[free_cnt], nallocs * sizeof(Chunk));
+
+
+		/* allocate alloc_cnt chunks at the end */
+		gettimeofday(&start_time, NULL);
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < alloc_cnt; i++)
+			chunks[nallocs + i].ptr = palloc(chunkSize);
+
+		gettimeofday(&end_time, NULL);
+
+		nallocs += alloc_cnt;
+
+		alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+					  (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+		MemoryContextSwitchTo(oldcxt);
+
+		mem_allocated = Max(mem_allocated, MemoryContextMemAllocated(cxt, true));
+	}
+
+	/* release the chunks in random order */
+	for (i = 0; i < nallocs; i++)
+		chunks[i].random = random();
+
+	qsort(chunks, nallocs, sizeof(Chunk), chunk_index_cmp);
+
+	gettimeofday(&start_time, NULL);
+
+	for (i = 0; i < nallocs; i++)
+		pfree(chunks[i].ptr);
+
+	gettimeofday(&end_time, NULL);
+
+	free_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+				 (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	values[0] = Int64GetDatum(mem_allocated);
+	values[1] = Int64GetDatum(alloc_time);
+	values[2] = Int64GetDatum(free_time);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	tuple = heap_form_tuple(tupdesc, values, nulls);
+	result = HeapTupleGetDatum(tuple);
+
+	PG_RETURN_DATUM(result);
+}
+
+Datum
+slab_bench_fifo(PG_FUNCTION_ARGS)
+{
+	MemoryContext	cxt,
+					oldcxt;
+	Chunk		   *chunks;
+	int64			i, j;
+	int64			nallocs = PG_GETARG_INT64(0);
+	int64			blockSize = PG_GETARG_INT64(1);
+	int64			chunkSize = PG_GETARG_INT64(2);
+
+	int				nloops = PG_GETARG_INT32(3);
+	int				free_cnt = PG_GETARG_INT32(4);
+	int				alloc_cnt = PG_GETARG_INT32(5);
+
+	struct timeval	start_time,
+					end_time;
+	int64			alloc_time = 0,
+					free_time = 0;
+	int64			mem_allocated;
+
+	TupleDesc		tupdesc;
+	Datum			result;
+	HeapTuple		tuple;
+	Datum			values[9];
+	bool			nulls[9];
+
+	int				maxchunks;
+
+	maxchunks = nallocs + nloops * Max(0, alloc_cnt - free_cnt);
+
+	cxt = SlabContextCreate(CurrentMemoryContext, "slab_bench", blockSize, chunkSize);
+
+	chunks = (Chunk *) palloc(maxchunks * sizeof(Chunk));
+
+	oldcxt = MemoryContextSwitchTo(cxt);
+
+	gettimeofday(&start_time, NULL);
+
+	for (i = 0; i < nallocs; i++)
+		chunks[i].ptr = palloc(chunkSize);
+
+	gettimeofday(&end_time, NULL);
+
+	alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+				  (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+	MemoryContextSwitchTo(oldcxt);
+
+	mem_allocated = MemoryContextMemAllocated(cxt, true);
+
+
+	/* do the requested number of free/alloc loops */
+	for (j = 0; j < nloops; j++)
+	{
+		oldcxt = MemoryContextSwitchTo(cxt);
+
+		gettimeofday(&start_time, NULL);
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < free_cnt; i++)
+			pfree(chunks[i].ptr);
+
+		gettimeofday(&end_time, NULL);
+
+		nallocs -= free_cnt;
+
+		free_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+					 (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+		memmove(chunks, &chunks[free_cnt], nallocs * sizeof(Chunk));
+
+		/* allocate alloc_cnt chunks at the end */
+		gettimeofday(&start_time, NULL);
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < alloc_cnt; i++)
+			chunks[nallocs + i].ptr = palloc(chunkSize);
+
+		gettimeofday(&end_time, NULL);
+
+		nallocs += alloc_cnt;
+
+		alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+					  (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+		MemoryContextSwitchTo(oldcxt);
+
+		mem_allocated = Max(mem_allocated, MemoryContextMemAllocated(cxt, true));
+	}
+
+
+	gettimeofday(&start_time, NULL);
+
+	for (i = 0; i < nallocs; i++)
+		pfree(chunks[i].ptr);
+
+	gettimeofday(&end_time, NULL);
+
+	free_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+				 (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	values[0] = Int64GetDatum(mem_allocated);
+	values[1] = Int64GetDatum(alloc_time);
+	values[2] = Int64GetDatum(free_time);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	tuple = heap_form_tuple(tupdesc, values, nulls);
+	result = HeapTupleGetDatum(tuple);
+
+	PG_RETURN_DATUM(result);
+}
+
+Datum
+slab_bench_lifo(PG_FUNCTION_ARGS)
+{
+	MemoryContext	cxt,
+					oldcxt;
+	Chunk		  *chunks;
+	int64			i, j;
+	int64			nallocs = PG_GETARG_INT64(0);
+	int64			blockSize = PG_GETARG_INT64(1);
+	int64			chunkSize = PG_GETARG_INT64(2);
+
+	int				nloops = PG_GETARG_INT32(3);
+	int				free_cnt = PG_GETARG_INT32(4);
+	int				alloc_cnt = PG_GETARG_INT32(5);
+
+	struct timeval	start_time,
+					end_time;
+	int64			alloc_time = 0,
+					free_time = 0;
+	int64			mem_allocated;
+
+	TupleDesc		tupdesc;
+	Datum			result;
+	HeapTuple		tuple;
+	Datum			values[9];
+	bool			nulls[9];
+
+	int				maxchunks;
+
+	maxchunks = nallocs + nloops * Max(0, alloc_cnt - free_cnt);
+
+	cxt = SlabContextCreate(CurrentMemoryContext, "slab_bench", blockSize, chunkSize);
+
+	chunks = (Chunk *) palloc(maxchunks * sizeof(Chunk));
+
+	oldcxt = MemoryContextSwitchTo(cxt);
+
+	/* palloc benchmark */
+	gettimeofday(&start_time, NULL);
+
+	for (i = 0; i < nallocs; i++)
+		chunks[i].ptr = palloc(chunkSize);
+
+	gettimeofday(&end_time, NULL);
+
+	alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+				  (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+	MemoryContextSwitchTo(oldcxt);
+
+	mem_allocated = MemoryContextMemAllocated(cxt, true);
+
+
+	/* do the requested number of free/alloc loops */
+	for (j = 0; j < nloops; j++)
+	{
+		oldcxt = MemoryContextSwitchTo(cxt);
+
+		gettimeofday(&start_time, NULL);
+
+		/* free the first free_cnt chunks */
+		for (i = 1; i <= free_cnt; i++)
+			pfree(chunks[nallocs - i].ptr);
+
+		gettimeofday(&end_time, NULL);
+
+		nallocs -= free_cnt;
+
+		free_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+					 (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+		/* allocate alloc_cnt chunks at the end */
+		gettimeofday(&start_time, NULL);
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < alloc_cnt; i++)
+			chunks[nallocs + i].ptr = palloc(chunkSize);
+
+		gettimeofday(&end_time, NULL);
+
+		nallocs += alloc_cnt;
+
+		alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+					  (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+		MemoryContextSwitchTo(oldcxt);
+
+		mem_allocated = Max(mem_allocated, MemoryContextMemAllocated(cxt, true));
+	}
+
+
+	gettimeofday(&start_time, NULL);
+
+	for (i = (nallocs - 1); i >= 0; i--)
+		pfree(chunks[i].ptr);
+
+	gettimeofday(&end_time, NULL);
+
+	free_time += (end_time.tv_sec - start_time.tv_sec) * 1000L +
+				 (end_time.tv_usec - start_time.tv_usec) / 1000L;
+
+	/* Build a tuple descriptor for our result type */
+	if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+		elog(ERROR, "return type must be a row type");
+
+	values[0] = Int64GetDatum(mem_allocated);
+	values[1] = Int64GetDatum(alloc_time);
+	values[2] = Int64GetDatum(free_time);
+
+	memset(nulls, 0, sizeof(nulls));
+
+	tuple = heap_form_tuple(tupdesc, values, nulls);
+	result = HeapTupleGetDatum(tuple);
+
+	MemoryContextDelete(cxt);
+
+	PG_RETURN_DATUM(result);
+}
diff --git a/contrib/slab_bench/slab_bench.control b/contrib/slab_bench/slab_bench.control
new file mode 100644
index 0000000000..290809c6f7
--- /dev/null
+++ b/contrib/slab_bench/slab_bench.control
@@ -0,0 +1,4 @@
+# slab_bench extension
+comment = 'functions for benchmarking slab context'
+default_version = '1.0'
+module_pathname = '$libdir/slab_bench'
-- 
2.31.1

