From 326d3dc3d636e3b3b03aa5a385ccd12a3bad581a 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/4] generation bench

---
 contrib/generation_bench/.gitignore           |   4 +
 contrib/generation_bench/Makefile             |  21 +
 contrib/generation_bench/bench.sql            |  88 ++++
 .../generation_bench--1.0.sql                 |  16 +
 contrib/generation_bench/generation_bench.c   | 449 ++++++++++++++++++
 .../generation_bench/generation_bench.control |   4 +
 6 files changed, 582 insertions(+)
 create mode 100644 contrib/generation_bench/.gitignore
 create mode 100644 contrib/generation_bench/Makefile
 create mode 100644 contrib/generation_bench/bench.sql
 create mode 100644 contrib/generation_bench/generation_bench--1.0.sql
 create mode 100644 contrib/generation_bench/generation_bench.c
 create mode 100644 contrib/generation_bench/generation_bench.control

diff --git a/contrib/generation_bench/.gitignore b/contrib/generation_bench/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/contrib/generation_bench/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/contrib/generation_bench/Makefile b/contrib/generation_bench/Makefile
new file mode 100644
index 0000000000..0fee5b84db
--- /dev/null
+++ b/contrib/generation_bench/Makefile
@@ -0,0 +1,21 @@
+# contrib/generation_bench/Makefile
+
+MODULE_big = generation_bench
+OBJS = generation_bench.o
+
+EXTENSION = generation_bench
+DATA = generation_bench--1.0.sql
+PGFILEDESC = "generation_bench - slab context benchmarking functions"
+
+REGRESS = generation_bench
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = contrib/generation_bench
+top_builddir = ../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/contrib/generation_bench/bench.sql b/contrib/generation_bench/bench.sql
new file mode 100644
index 0000000000..05f065ace5
--- /dev/null
+++ b/contrib/generation_bench/bench.sql
@@ -0,0 +1,88 @@
+CREATE EXTENSION generation_bench;
+
+\o fifo-no-loops.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+
+\o lifo-no-loops.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+
+\o random-no-loops.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 0, 0, 0) x;
+
+
+\o fifo-increase.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+
+\o lifo-increase.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 15000) x;
+
+\o random-increase.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 1500) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 1500) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 1500) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 1500) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 1500) x;
+
+
+\o fifo-decrease.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+
+\o lifo-decrease.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000000, block_size, chunk_size, 2*chunk_size, 100, 10000, 5000) x;
+
+\o random-decrease.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 500) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 500) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 500) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 500) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000000, block_size, chunk_size, 2*chunk_size, 100, 1000, 500) x;
+
+
+\o fifo-cycle.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_fifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+
+\o lifo-cycle.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_lifo(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+
+\o random-cycle.data
+select 1 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 2 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 3 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 4 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
+select 5 as run, block_size, chunk_size, 2*chunk_size, 1000000 * chunk_size, x.* from generate_series(32,512,32) a(chunk_size), (values (1024), (2048), (4096), (8192), (16384), (32768)) AS b(block_size), lateral generation_bench_random(1000, block_size, chunk_size, 2*chunk_size, 1000, 1000, 1000) x;
diff --git a/contrib/generation_bench/generation_bench--1.0.sql b/contrib/generation_bench/generation_bench--1.0.sql
new file mode 100644
index 0000000000..5f7c46eef2
--- /dev/null
+++ b/contrib/generation_bench/generation_bench--1.0.sql
@@ -0,0 +1,16 @@
+/* generation_bench--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION generation_bench" to load this file. \quit
+
+CREATE FUNCTION generation_bench_random(nallocs bigint, block_size bigint, min_alloc_size bigint, max_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', 'generation_bench_random'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION generation_bench_fifo(nallocs bigint, block_size bigint, min_alloc_size bigint, max_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', 'generation_bench_fifo'
+LANGUAGE C VOLATILE STRICT;
+
+CREATE FUNCTION generation_bench_lifo(nallocs bigint, block_size bigint, min_alloc_size bigint, max_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', 'generation_bench_lifo'
+LANGUAGE C VOLATILE STRICT;
diff --git a/contrib/generation_bench/generation_bench.c b/contrib/generation_bench/generation_bench.c
new file mode 100644
index 0000000000..f86aaaa041
--- /dev/null
+++ b/contrib/generation_bench/generation_bench.c
@@ -0,0 +1,449 @@
+/*-------------------------------------------------------------------------
+ *
+ * generation_bench.c
+ *
+ * helper functions to benchmark generation context with different workloads
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <sys/time.h>
+#include <x86intrin.h>
+
+#include "funcapi.h"
+#include "miscadmin.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(generation_bench_random);
+PG_FUNCTION_INFO_V1(generation_bench_fifo);
+PG_FUNCTION_INFO_V1(generation_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;
+}
+
+#ifdef RDTSC
+// optional wrapper if you don't want to just use __rdtsc() everywhere
+static inline
+unsigned long long read_time(void)
+{
+	// _mm_lfence();  // optionally wait for earlier insns to retire before reading the clock
+	return __rdtsc();
+	// _mm_lfence();  // optionally block later instructions until rdtsc retires
+}
+#else
+static inline
+unsigned long long read_time(void)
+{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+
+	return (unsigned long long) tv.tv_sec * 1000000L + tv.tv_usec;
+}
+#endif
+
+Datum
+generation_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			minChunkSize = PG_GETARG_INT64(2);
+	int64			maxChunkSize = PG_GETARG_INT64(3);
+
+	int				nloops = PG_GETARG_INT32(4);
+	int				free_cnt = PG_GETARG_INT32(5);
+	int				alloc_cnt = PG_GETARG_INT32(6);
+
+	unsigned long long	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 = GenerationContextCreate(CurrentMemoryContext, "generation_bench", blockSize);
+
+	chunks = (Chunk *) palloc(maxchunks * sizeof(Chunk));
+
+	/* allocate the chunks in random order */
+	oldcxt = MemoryContextSwitchTo(cxt);
+
+	start_time = read_time();
+
+	for (i = 0; i < nallocs; i++)
+	{
+		int chunkSize = minChunkSize + random() % (maxChunkSize - minChunkSize);
+
+		chunks[i].ptr = palloc(chunkSize);
+	}
+
+	end_time = read_time();
+
+	alloc_time += (end_time - start_time);
+
+	MemoryContextSwitchTo(oldcxt);
+
+	mem_allocated = MemoryContextMemAllocated(cxt, true);
+
+	/* do the requested number of free/alloc loops */
+	for (j = 0; j < nloops; j++)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		/* randomize the indexes */
+		for (i = 0; i < nallocs; i++)
+			chunks[i].random = random();
+
+		qsort(chunks, nallocs, sizeof(Chunk), chunk_index_cmp);
+
+		oldcxt = MemoryContextSwitchTo(cxt);
+
+		start_time = read_time();
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < Min(nallocs, free_cnt); i++)
+			pfree(chunks[i].ptr);
+
+		end_time = read_time();
+
+		nallocs -= Min(nallocs, free_cnt);
+
+		free_time += (end_time - start_time);
+
+		memmove(chunks, &chunks[free_cnt], nallocs * sizeof(Chunk));
+
+
+		/* allocate alloc_cnt chunks at the end */
+		start_time = read_time();
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < alloc_cnt; i++)
+		{
+			int chunkSize = minChunkSize + random() % (maxChunkSize - minChunkSize);
+
+			chunks[nallocs + i].ptr = palloc(chunkSize);
+		}
+
+		end_time = read_time();
+
+		nallocs += alloc_cnt;
+
+		alloc_time += (end_time - start_time);
+
+		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);
+
+	start_time = read_time();
+
+	for (i = 0; i < nallocs; i++)
+		pfree(chunks[i].ptr);
+
+	end_time = read_time();
+
+	free_time += (end_time - start_time);
+
+	/* 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
+generation_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			minChunkSize = PG_GETARG_INT64(2);
+	int64			maxChunkSize = PG_GETARG_INT64(3);
+
+	int				nloops = PG_GETARG_INT32(4);
+	int				free_cnt = PG_GETARG_INT32(5);
+	int				alloc_cnt = PG_GETARG_INT32(6);
+
+	unsigned long long	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 = GenerationContextCreate(CurrentMemoryContext, "generation_bench", blockSize);
+
+	chunks = (Chunk *) palloc(maxchunks * sizeof(Chunk));
+
+	oldcxt = MemoryContextSwitchTo(cxt);
+
+	start_time = read_time();
+
+	for (i = 0; i < nallocs; i++)
+	{
+		int chunkSize = minChunkSize + random() % (maxChunkSize - minChunkSize);
+
+		chunks[i].ptr = palloc(chunkSize);
+	}
+
+	end_time = read_time();
+
+	alloc_time += (end_time - start_time);
+
+	MemoryContextSwitchTo(oldcxt);
+
+	mem_allocated = MemoryContextMemAllocated(cxt, true);
+
+
+	/* do the requested number of free/alloc loops */
+	for (j = 0; j < nloops; j++)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		oldcxt = MemoryContextSwitchTo(cxt);
+
+		start_time = read_time();
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < Min(nallocs, free_cnt); i++)
+			pfree(chunks[i].ptr);
+
+		end_time = read_time();
+
+		nallocs -= Min(nallocs, free_cnt);
+
+		free_time += (end_time - start_time);
+
+		memmove(chunks, &chunks[free_cnt], nallocs * sizeof(Chunk));
+
+		/* allocate alloc_cnt chunks at the end */
+		start_time = read_time();
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < alloc_cnt; i++)
+		{
+			int chunkSize = minChunkSize + random() % (maxChunkSize - minChunkSize);
+
+			chunks[nallocs + i].ptr = palloc(chunkSize);
+		}
+
+		end_time = read_time();
+
+		nallocs += alloc_cnt;
+
+		alloc_time += (end_time - start_time);
+
+		MemoryContextSwitchTo(oldcxt);
+
+		mem_allocated = Max(mem_allocated, MemoryContextMemAllocated(cxt, true));
+	}
+
+	start_time = read_time();
+
+	for (i = 0; i < nallocs; i++)
+		pfree(chunks[i].ptr);
+
+	end_time = read_time();
+
+	free_time += (end_time - start_time);
+
+	/* 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
+generation_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			minChunkSize = PG_GETARG_INT64(2);
+	int64			maxChunkSize = PG_GETARG_INT64(3);
+
+	int				nloops = PG_GETARG_INT32(4);
+	int				free_cnt = PG_GETARG_INT32(5);
+	int				alloc_cnt = PG_GETARG_INT32(6);
+
+	unsigned long long	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 = GenerationContextCreate(CurrentMemoryContext, "generation_bench", blockSize);
+
+	chunks = (Chunk *) palloc(maxchunks * sizeof(Chunk));
+
+	oldcxt = MemoryContextSwitchTo(cxt);
+
+	/* palloc benchmark */
+	start_time = read_time();
+
+	for (i = 0; i < nallocs; i++)
+	{
+		int chunkSize = minChunkSize + random() % (maxChunkSize - minChunkSize);
+
+		chunks[i].ptr = palloc(chunkSize);
+	}
+
+	end_time = read_time();
+
+	alloc_time += (end_time - start_time);
+
+	MemoryContextSwitchTo(oldcxt);
+
+	mem_allocated = MemoryContextMemAllocated(cxt, true);
+
+
+	/* do the requested number of free/alloc loops */
+	for (j = 0; j < nloops; j++)
+	{
+		CHECK_FOR_INTERRUPTS();
+
+		oldcxt = MemoryContextSwitchTo(cxt);
+
+		start_time = read_time();
+
+		/* free the first free_cnt chunks */
+		for (i = 1; i <= Min(nallocs, free_cnt); i++)
+			pfree(chunks[nallocs - i].ptr);
+
+		end_time = read_time();
+
+		nallocs -= Min(nallocs, free_cnt);
+
+		free_time += (end_time - start_time);
+
+		/* allocate alloc_cnt chunks at the end */
+		start_time = read_time();
+
+		/* free the first free_cnt chunks */
+		for (i = 0; i < alloc_cnt; i++)
+		{
+			int chunkSize = minChunkSize + random() % (maxChunkSize - minChunkSize);
+
+			chunks[nallocs + i].ptr = palloc(chunkSize);
+		}
+
+		end_time = read_time();
+
+		nallocs += alloc_cnt;
+
+		alloc_time += (end_time - start_time);
+
+		MemoryContextSwitchTo(oldcxt);
+
+		mem_allocated = Max(mem_allocated, MemoryContextMemAllocated(cxt, true));
+	}
+
+
+	start_time = read_time();
+
+	for (i = (nallocs - 1); i >= 0; i--)
+		pfree(chunks[i].ptr);
+
+	end_time = read_time();
+
+	free_time += (end_time - start_time);
+
+	/* 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/generation_bench/generation_bench.control b/contrib/generation_bench/generation_bench.control
new file mode 100644
index 0000000000..8c81e22e6b
--- /dev/null
+++ b/contrib/generation_bench/generation_bench.control
@@ -0,0 +1,4 @@
+# generation_bench extension
+comment = 'functions for benchmarking generation context'
+default_version = '1.0'
+module_pathname = '$libdir/generation_bench'
-- 
2.31.1

