From b65320736cf63684b4710b3d63e243a0862d37c6 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent Date: Fri, 5 Apr 2024 15:13:52 +0200 Subject: [PATCH] repalloc performance test function --- src/backend/utils/adt/mcxtfuncs.c | 231 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.dat | 12 ++ 2 files changed, 243 insertions(+) diff --git a/src/backend/utils/adt/mcxtfuncs.c b/src/backend/utils/adt/mcxtfuncs.c index 4d4a70915b..88de4bbcb5 100644 --- a/src/backend/utils/adt/mcxtfuncs.c +++ b/src/backend/utils/adt/mcxtfuncs.c @@ -15,8 +15,11 @@ #include "postgres.h" +#include + #include "funcapi.h" #include "mb/pg_wchar.h" +#include "miscadmin.h" #include "storage/proc.h" #include "storage/procarray.h" #include "utils/builtins.h" @@ -185,3 +188,231 @@ pg_log_backend_memory_contexts(PG_FUNCTION_ARGS) PG_RETURN_BOOL(true); } + +typedef struct AllocateTestNext +{ + struct AllocateTestNext *next; /* ptr to the next allocation */ +} AllocateTestNext; + +/* #define ALLOCATE_TEST_DEBUG */ +/* + * pg_allocate_memory_test + * Used to test the performance of a memory context types + */ +Datum +pg_allocate_memory_test(PG_FUNCTION_ARGS) +{ + int32 chunk_size = PG_GETARG_INT32(0); + int64 keep_memory = PG_GETARG_INT64(1); + int64 total_alloc = PG_GETARG_INT64(2); + text *context_type_text = PG_GETARG_TEXT_PP(3); + char *context_type; + int64 curr_memory_use = 0; + int64 remaining_alloc_bytes = total_alloc; + MemoryContext context; + MemoryContext oldContext; + AllocateTestNext *next_free_ptr = NULL; + AllocateTestNext *last_alloc = NULL; + clock_t start, end; + + if (chunk_size < sizeof(AllocateTestNext)) + elog(ERROR, "chunk_size (%d) must be at least %ld bytes", chunk_size, + sizeof(AllocateTestNext)); + if (keep_memory > total_alloc) + elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")", + keep_memory, total_alloc); + + context_type = text_to_cstring(context_type_text); + + start = clock(); + + if (strcmp(context_type, "generation") == 0) + context = GenerationContextCreate(CurrentMemoryContext, + "pg_allocate_memory_test", + ALLOCSET_DEFAULT_SIZES); + else if (strcmp(context_type, "aset") == 0) + context = AllocSetContextCreate(CurrentMemoryContext, + "pg_allocate_memory_test", + ALLOCSET_DEFAULT_SIZES); + else if (strcmp(context_type, "slab") == 0) + context = SlabContextCreate(CurrentMemoryContext, + "pg_allocate_memory_test", + ALLOCSET_DEFAULT_MAXSIZE, + chunk_size); + else + elog(ERROR, "context_type must be \"generation\", \"aset\" or \"slab\""); + + oldContext = MemoryContextSwitchTo(context); + + while (remaining_alloc_bytes > 0) + { + AllocateTestNext *curr_alloc; + + CHECK_FOR_INTERRUPTS(); + + /* Allocate the memory and update the counters */ + curr_alloc = (AllocateTestNext *) palloc(chunk_size); + remaining_alloc_bytes -= chunk_size; + curr_memory_use += chunk_size; + +#ifdef ALLOCATE_TEST_DEBUG + elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes); +#endif + + /* + * Point the last allocate to this one so that we can free allocations + * starting with the oldest first. + */ + curr_alloc->next = NULL; + if (last_alloc != NULL) + last_alloc->next = curr_alloc; + + if (next_free_ptr == NULL) + { + /* + * Remember the first chunk to free. We will follow the ->next + * pointers to find the next chunk to free when freeing memory + */ + next_free_ptr = curr_alloc; + } + + /* + * If the currently allocated memory has reached or exceeded the amount + * of memory we want to keep allocated at once then we'd better free + * some. Since all allocations are the same size we only need to free + * one allocation per loop. + */ + if (curr_memory_use >= keep_memory) + { + AllocateTestNext *next = next_free_ptr->next; + + /* free the memory and update the current memory usage */ + pfree(next_free_ptr); + curr_memory_use -= chunk_size; + +#ifdef ALLOCATE_TEST_DEBUG + elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes); +#endif + /* get the next chunk to free */ + next_free_ptr = next; + } + + if (curr_memory_use > 0) + last_alloc = curr_alloc; + else + last_alloc = NULL; + } + + /* cleanup loop -- pfree remaining memory */ + while (next_free_ptr != NULL) + { + AllocateTestNext *next = next_free_ptr->next; + + /* free the memory and update the current memory usage */ + pfree(next_free_ptr); + curr_memory_use -= chunk_size; + +#ifdef ALLOCATE_TEST_DEBUG + elog(NOTICE, "free %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", next_free_ptr, curr_memory_use, remaining_alloc_bytes); +#endif + + next_free_ptr = next; + } + + MemoryContextSwitchTo(oldContext); + + end = clock(); + + PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC); +} + +Datum +pg_allocate_memory_test_reset(PG_FUNCTION_ARGS) +{ + int32 chunk_size = PG_GETARG_INT32(0); + int64 keep_memory = PG_GETARG_INT64(1); + int64 total_alloc = PG_GETARG_INT64(2); + int64 n_ptrs; + text *context_type_text = PG_GETARG_TEXT_PP(3); + char *context_type; + int64 curr_memory_use = 0; + int64 remaining_alloc_bytes = total_alloc; + MemoryContext context; + MemoryContext oldContext; + clock_t start, end; + int64 **ptrs; + uint32 lcg_value = 0; + uint32 lcg_c = 1013904223; + uint32 lcg_a = 1664525; + + if (chunk_size < 1) + elog(ERROR, "size of chunk must be above 0"); + if (keep_memory > total_alloc) + elog(ERROR, "keep_memory (" INT64_FORMAT ") must be less than total_alloc (" INT64_FORMAT ")", + keep_memory, total_alloc); + + context_type = text_to_cstring(context_type_text); + n_ptrs = 1 << pg_leftmost_one_pos64(keep_memory / chunk_size); + + ptrs = (int64 **) palloc0(n_ptrs * sizeof(int64 *)); + + start = clock(); + + if (strcmp(context_type, "generation") == 0) + context = GenerationContextCreate(CurrentMemoryContext, + "pg_allocate_memory_test", + ALLOCSET_DEFAULT_SIZES); + else if (strcmp(context_type, "aset") == 0) + context = AllocSetContextCreate(CurrentMemoryContext, + "pg_allocate_memory_test", + ALLOCSET_DEFAULT_SIZES); + else if (strcmp(context_type, "slab") == 0) + context = SlabContextCreate(CurrentMemoryContext, + "pg_allocate_memory_test", + ALLOCSET_DEFAULT_MAXSIZE, + chunk_size); +// else if (strcmp(context_type, "bump") == 0) +// context = BumpContextCreate(CurrentMemoryContext, +// "pg_allocate_memory_test", +// ALLOCSET_DEFAULT_SIZES); + else + elog(ERROR, "context_type must be \"generation\", \"aset\" or \"slab\""); + + oldContext = MemoryContextSwitchTo(context); + + for (int i = 0; i < n_ptrs && remaining_alloc_bytes > 0;) + { + CHECK_FOR_INTERRUPTS(); + + /* Allocate the memory and update the counters */ + ptrs[i++] = (int64 *) palloc(chunk_size); + remaining_alloc_bytes -= chunk_size; + curr_memory_use += chunk_size; + } + + while (remaining_alloc_bytes > 0) + { + uint32 index; + CHECK_FOR_INTERRUPTS(); + index = lcg_value % (uint32) n_ptrs; + lcg_value = lcg_value * lcg_a + lcg_c; + Assert(PointerIsValid(ptr)); + + /* reallocate, and update the counters */ + ptrs[index] = (int64 *) repalloc(ptrs[index], chunk_size); + + remaining_alloc_bytes -= chunk_size; + curr_memory_use += chunk_size; + +#ifdef ALLOCATE_TEST_DEBUG + elog(NOTICE, "alloc %p (curr_memory_use " INT64_FORMAT " bytes, remaining_alloc_bytes " INT64_FORMAT ")", curr_alloc, curr_memory_use, remaining_alloc_bytes); +#endif + } + + MemoryContextSwitchTo(oldContext); + MemoryContextDelete(context); + + end = clock(); + + PG_RETURN_FLOAT8((double) (end - start) / CLOCKS_PER_SEC); +} diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 153d816a05..15d191db8b 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -8288,6 +8288,18 @@ prorettype => 'bool', proargtypes => 'int4', prosrc => 'pg_log_backend_memory_contexts' }, +# just for testing memory context allocation speed +{ oid => '9319', descr => 'for testing performance of allocation and freeing', + proname => 'pg_allocate_memory_test', provolatile => 'v', + prorettype => 'float8', proargtypes => 'int4 int8 int8 text', + prosrc => 'pg_allocate_memory_test' }, + +# just for testing memory context allocation speed +{ oid => '9320', descr => 'for testing performance of allocation and resetting', + proname => 'pg_allocate_memory_test_reset', provolatile => 'v', + prorettype => 'float8', proargtypes => 'int4 int8 int8 text', + prosrc => 'pg_allocate_memory_test_reset' }, + # non-persistent series generator { oid => '1066', descr => 'non-persistent series generator', proname => 'generate_series', prorows => '1000', -- 2.40.1