alloc_bench_contrib.patch
text/plain
Filename: alloc_bench_contrib.patch
Type: text/plain
Part: 1
Patch
Format: unified
| File | + | − |
|---|---|---|
| contrib/alloc_bench/alloc_bench--1.0.sql | 9 | 0 |
| contrib/alloc_bench/alloc_bench.c | 199 | 0 |
| contrib/alloc_bench/alloc_bench.control | 4 | 0 |
| contrib/alloc_bench/bench.sql | 40 | 0 |
| contrib/alloc_bench/.gitignore | 4 | 0 |
| contrib/alloc_bench/Makefile | 21 | 0 |
diff --git a/contrib/alloc_bench/.gitignore b/contrib/alloc_bench/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/alloc_bench/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/alloc_bench/Makefile b/contrib/alloc_bench/Makefile
new file mode 100644
index 0000000000..c672b29bdb
--- /dev/null
+++ b/contrib/alloc_bench/Makefile
@@ -0,0 +1,21 @@
+# contrib/alloc_bench/Makefile
+
+MODULE_big = alloc_bench
+OBJS = alloc_bench.o
+
+EXTENSION = alloc_bench
+DATA = alloc_bench--1.0.sql
+PGFILEDESC = "alloc_bench - memory context benchmarking functions"
+
+REGRESS = alloc_bench
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/alloc_bench
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/alloc_bench/alloc_bench--1.0.sql b/contrib/alloc_bench/alloc_bench--1.0.sql
new file mode 100644
index 0000000000..2ca604a682
--- /dev/null
+++ b/contrib/alloc_bench/alloc_bench--1.0.sql
@@ -0,0 +1,9 @@
+/* alloc_bench--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION alloc_bench" to load this file. \quit
+
+CREATE FUNCTION alloc_bench(context_type text, pattern text, nchunks bigint, block_size bigint, chunk_size bigint, nloops int, random_segments int, out mem_allocated bigint, out alloc_us bigint, out free_us bigint)
+AS 'MODULE_PATHNAME', 'alloc_bench'
+LANGUAGE C VOLATILE STRICT;
+
diff --git a/contrib/alloc_bench/alloc_bench.c b/contrib/alloc_bench/alloc_bench.c
new file mode 100644
index 0000000000..f76501c053
--- /dev/null
+++ b/contrib/alloc_bench/alloc_bench.c
@@ -0,0 +1,199 @@
+/*-------------------------------------------------------------------------
+ *
+ * alloc_bench.c
+ *
+ * helper functions to benchmark memory contexts with different workloads
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/time.h>
+
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(alloc_bench);
+
+typedef enum AllocationPattern {
+ FIFO,
+ LIFO
+} AllocationPattern;
+
+static int random_segment_size;
+static int random_fifo = 0;
+
+static int
+int64_random_compare(const void *a, const void *b)
+{
+ const int64 ia = *(const int64 *) a;
+ const int64 ib = *(const int64 *) b;
+ int rss = random_segment_size;
+
+ if ((ia / rss * rss) + (random() % rss) > ib)
+ return random_fifo == 0 ? -1 : 1;
+ else
+ return random_fifo == 0 ? 1 : -1;
+}
+
+Datum
+alloc_bench(PG_FUNCTION_ARGS)
+{
+ MemoryContext cxt,
+ oldcxt;
+ void **chunks;
+ int64 *indexes;
+ int64 j;
+ char *context_type;
+ text *context_type_text = PG_GETARG_TEXT_PP(0);
+ char *pattern_str;
+ text *pattern_text = PG_GETARG_TEXT_PP(1);
+ int64 nchunks = PG_GETARG_INT64(2);
+ int64 blockSize = PG_GETARG_INT64(3);
+ int64 chunkSize = PG_GETARG_INT64(4);
+
+ int nloops = PG_GETARG_INT32(5);
+ int random_segments = PG_GETARG_INT32(6);
+
+ 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];
+ AllocationPattern pattern;
+
+ context_type = text_to_cstring(context_type_text);
+
+ if (strcmp(context_type, "generation") == 0)
+ cxt = GenerationContextCreate(CurrentMemoryContext,
+ "alloc_bench",
+ ALLOCSET_DEFAULT_SIZES);
+ else if (strcmp(context_type, "aset") == 0)
+ cxt = AllocSetContextCreate(CurrentMemoryContext,
+ "alloc_bench",
+ ALLOCSET_DEFAULT_SIZES);
+ else if (strcmp(context_type, "slab") == 0)
+ cxt = SlabContextCreate(CurrentMemoryContext,
+ "alloc_bench",
+ blockSize,
+ chunkSize);
+ else
+ elog(ERROR, "%s is not a valid context type. context_type must be \"generation\", \"aset\" or \"slab\"",
+ context_type);
+
+ pattern_str = text_to_cstring(pattern_text);
+
+ if (strcmp(pattern_str, "fifo") == 0)
+ pattern = FIFO;
+ else if (strcmp(pattern_str, "lifo") == 0)
+ pattern = LIFO;
+ else
+ elog(ERROR, "%s is not a valid allocation pattern. Must be \"fifo\" or \"lifo\"",
+ pattern_str);
+
+ chunks = (void **) palloc(nchunks * sizeof(void *));
+ indexes = (int64 *) palloc(nchunks * sizeof(int64));
+
+ /* set the indexes so according to the allocation pattern */
+ switch (pattern)
+ {
+ case FIFO:
+ for (int64 i = 0; i < nchunks; i++)
+ indexes[i] = i;
+ break;
+ case LIFO:
+ for (int64 i = 0; i < nchunks; i++)
+ indexes[i] = nchunks - i - 1;
+ break;
+ }
+
+ /*
+ * When a non-zero random_segments is specified, we randomize the
+ * order of the pfree's so they're random within their own segment.
+ * This means we don't entirely randomize the order, but if there are
+ * say, 10 segments and 60 chunks, we only randomize the first 6
+ * chunks then the next 6. Specifiying a lower number of
+ * random_segments means the FIFO or LIFO pattern becomes more random.
+ */
+ if (random_segments > 0)
+ {
+ random_segment_size = nchunks / random_segments;
+ random_fifo = (pattern == FIFO);
+ qsort(indexes, nchunks, sizeof(int64), int64_random_compare);
+ }
+
+ mem_allocated = 0;
+
+ oldcxt = MemoryContextSwitchTo(cxt);
+
+ /* do the requested number of pfree/palloc loops */
+ for (j = 0; j < nloops; j++)
+ {
+ CHECK_FOR_INTERRUPTS();
+
+ gettimeofday(&start_time, NULL);
+
+ /*
+ * We do palloc0 instead of palloc to simulate touching the
+ * memory. This will load cachelines, which is likely more
+ * realistic than just allocating memory and not doing
+ * anything with it.
+ */
+ for (int64 i = 0; i < nchunks; i++)
+ chunks[i] = palloc0(chunkSize);
+
+ gettimeofday(&end_time, NULL);
+
+ alloc_time += (end_time.tv_sec - start_time.tv_sec) * 1000000L +
+ (end_time.tv_usec - start_time.tv_usec);
+
+ gettimeofday(&start_time, NULL);
+
+ for (int64 i = 0; i < nchunks; i++)
+ {
+ char *ptr = (char *) chunks[indexes[i]];
+
+ /*
+ * Free the chunk, but first touch the first cacheline
+ * of the chunk to simulate that we've just done
+ * something real with this memory.
+ */
+ if (ptr[0] == '\0')
+ pfree(ptr);
+ }
+
+ gettimeofday(&end_time, NULL);
+
+ free_time += (end_time.tv_sec - start_time.tv_sec) * 1000000L +
+ (end_time.tv_usec - start_time.tv_usec);
+
+ mem_allocated = Max(mem_allocated, MemoryContextMemAllocated(cxt, true));
+ }
+
+ MemoryContextSwitchTo(oldcxt);
+ MemoryContextDelete(cxt);
+
+ /* 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);
+}
+
diff --git a/contrib/alloc_bench/alloc_bench.control b/contrib/alloc_bench/alloc_bench.control
new file mode 100644
index 0000000000..597480ed5e
--- /dev/null
+++ b/contrib/alloc_bench/alloc_bench.control
@@ -0,0 +1,4 @@
+# alloc_bench extension
+comment = 'functions for benchmarking memory context'
+default_version = '1.0'
+module_pathname = '$libdir/alloc_bench'
diff --git a/contrib/alloc_bench/bench.sql b/contrib/alloc_bench/bench.sql
new file mode 100644
index 0000000000..d476a386c2
--- /dev/null
+++ b/contrib/alloc_bench/bench.sql
@@ -0,0 +1,40 @@
+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;
+
+
+\o fifo-cycle.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(1000, block_size, chunk_size, 10000, 1000, 1000) x;
+
+\o lifo-cycle.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(1000, block_size, chunk_size, 10000, 1000, 1000) x;
+
+\o random-cycle.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(1000, block_size, chunk_size, 10000, 1000, 1000) x;