MDEV-35750 Change MEM_ROOT allocation sizes to reduse calls to malloc() and avoid memory fragmentation

This commit updates default memory allocations size used with MEM_ROOT
objects to minimize the number of calls to malloc().

Changes:
- Updated MEM_ROOT block sizes in sql_const.h
- Updated MALLOC_OVERHEAD to also take into account the extra memory
  allocated by my_malloc()
- Updated init_alloc_root() to only take MALLOC_OVERHEAD into account as
  buffer size, not MALLOC_OVERHEAD + sizeof(USED_MEM).
- Reset mem_root->first_block_usage if and only if first block was used.
- Increase MEM_ROOT buffers sized used by my_load_defaults, plugin_init,
  Create_tmp_table, allocate_table_share, TABLE and TABLE_SHARE.
  This decreases number of malloc calls during queries.
- Use a small buffer for THD->main_mem_root in THD::THD. This avoids
  multiple malloc() call for new connections.

I tried the above changes on a complex select query with 12 tables.
The following shows the number of extra allocations that where used
to increase the size of the MEM_ROOT buffers.

Original code:
- Connection to MariaDB:   9 allocations
- First query run:       146 allocations
- Second query run:       24 allocations

Max memory allocated for thd when using with heap table:  61,262,408
Max memory allocated for thd when using Aria tmp table:      419,464

After changes:
Connection to MariaDB:     0 allocations
- First run:              25 allocations
- Second run:              7 allocations

Max memory allocated for thd when using with heap table:  61,347,424
Max memory allocated for thd when using Aria table:          529,168

The new code uses slightly more memory, but avoids memory fragmentation
and is slightly faster thanks to much fewer calls to malloc().

Reviewed-by: Sergei Golubchik <serg@mariadb.org>
This commit is contained in:
Monty 2024-11-22 14:23:57 +02:00
parent f297623345
commit e600f9aebb
20 changed files with 98 additions and 76 deletions

View file

@ -667,7 +667,7 @@ typedef SOCKET_SIZE_TYPE size_socket;
*/
#define IO_SIZE 4096U
/*
How much overhead does malloc have. The code often allocates
How much overhead does malloc/my_malloc have. The code often allocates
something like 1024-MALLOC_OVERHEAD bytes
*/
#define MALLOC_OVERHEAD (8+24)

View file

@ -878,6 +878,7 @@ extern void my_free_lock(void *ptr);
#endif
#define alloc_root_inited(A) ((A)->min_malloc != 0)
#define ALLOC_ROOT_MIN_BLOCK_SIZE (MALLOC_OVERHEAD + sizeof(USED_MEM) + 8)
#define DEFAULT_ROOT_BLOCK_SIZE 1024
#define clear_alloc_root(A) do { (A)->free= (A)->used= (A)->pre_alloc= 0; (A)->min_malloc=0;} while(0)
extern void init_alloc_root(PSI_memory_key key, MEM_ROOT *mem_root,
size_t block_size, size_t pre_alloc_size,

View file

@ -1785,14 +1785,14 @@ profiling-history-size 15
progress-report-time 5
protocol-version 10
proxy-protocol-networks
query-alloc-block-size 16384
query-alloc-block-size 32768
query-cache-limit 1048576
query-cache-min-res-unit 4096
query-cache-size 1048576
query-cache-strip-comments FALSE
query-cache-type OFF
query-cache-wlock-invalidate FALSE
query-prealloc-size 24576
query-prealloc-size 32768
range-alloc-block-size 4096
read-binlog-speed-limit 0
read-buffer-size 131072

View file

@ -299,8 +299,8 @@ SHOW VARIABLES WHERE variable_name IN ('range_alloc_block_size',
'query_alloc_block_size', 'query_prealloc_size',
'transaction_alloc_block_size', 'transaction_prealloc_size');
Variable_name Value
query_alloc_block_size 16384
query_prealloc_size 24576
query_alloc_block_size 32768
query_prealloc_size 32768
range_alloc_block_size 4096
transaction_alloc_block_size 8192
transaction_prealloc_size 4096
@ -310,8 +310,8 @@ WHERE variable_name IN ('range_alloc_block_size',
'query_alloc_block_size', 'query_prealloc_size',
'transaction_alloc_block_size', 'transaction_prealloc_size') ORDER BY 1;
VARIABLE_NAME VARIABLE_VALUE
QUERY_ALLOC_BLOCK_SIZE 16384
QUERY_PREALLOC_SIZE 24576
QUERY_ALLOC_BLOCK_SIZE 32768
QUERY_PREALLOC_SIZE 32768
RANGE_ALLOC_BLOCK_SIZE 4096
TRANSACTION_ALLOC_BLOCK_SIZE 8192
TRANSACTION_PREALLOC_SIZE 4096
@ -392,8 +392,8 @@ SHOW VARIABLES WHERE variable_name IN ('range_alloc_block_size',
'query_alloc_block_size', 'query_prealloc_size',
'transaction_alloc_block_size', 'transaction_prealloc_size');
Variable_name Value
query_alloc_block_size 16384
query_prealloc_size 24576
query_alloc_block_size 32768
query_prealloc_size 32768
range_alloc_block_size 4096
transaction_alloc_block_size 8192
transaction_prealloc_size 4096

View file

@ -22,13 +22,13 @@ Warnings:
Warning 1292 Truncated incorrect aria_sort_buffer_size value: '10'
select @@global.aria_sort_buffer_size;
@@global.aria_sort_buffer_size
16376
16352
set session aria_sort_buffer_size=10;
Warnings:
Warning 1292 Truncated incorrect aria_sort_buffer_size value: '10'
select @@session.aria_sort_buffer_size;
@@session.aria_sort_buffer_size
16376
16352
set global aria_sort_buffer_size=1.1;
ERROR 42000: Incorrect argument type to variable 'aria_sort_buffer_size'
set session aria_sort_buffer_size=1e1;
@ -40,7 +40,7 @@ Warnings:
Warning 1292 Truncated incorrect aria_sort_buffer_size value: '0'
select @@global.aria_sort_buffer_size;
@@global.aria_sort_buffer_size
16376
16352
set session aria_sort_buffer_size=cast(-1 as unsigned int);
select @@session.aria_sort_buffer_size;
@@session.aria_sort_buffer_size

View file

@ -1,11 +1,11 @@
SET @start_global_value = @@global.query_alloc_block_size;
SELECT @start_global_value;
@start_global_value
16384
32768
SET @start_session_value = @@session.query_alloc_block_size;
SELECT @start_session_value;
@start_session_value
16384
32768
'#--------------------FN_DYNVARS_130_01-------------------------#'
SET @@global.query_alloc_block_size = 10000;
Warnings:
@ -13,22 +13,22 @@ Warning 1292 Truncated incorrect query_alloc_block_size value: '10000'
SET @@global.query_alloc_block_size = DEFAULT;
SELECT @@global.query_alloc_block_size;
@@global.query_alloc_block_size
16384
32768
SET @@session.query_alloc_block_size = 20000;
Warnings:
Warning 1292 Truncated incorrect query_alloc_block_size value: '20000'
SET @@session.query_alloc_block_size = DEFAULT;
SELECT @@session.query_alloc_block_size;
@@session.query_alloc_block_size
16384
32768
'#--------------------FN_DYNVARS_130_02-------------------------#'
SET @@global.query_alloc_block_size = DEFAULT;
SELECT @@global.query_alloc_block_size = 16384;
@@global.query_alloc_block_size = 16384
SELECT @@global.query_alloc_block_size = 32768;
@@global.query_alloc_block_size = 32768
1
SET @@session.query_alloc_block_size = DEFAULT;
SELECT @@session.query_alloc_block_size = 16384;
@@session.query_alloc_block_size = 16384
SELECT @@session.query_alloc_block_size = 32768;
@@session.query_alloc_block_size = 32768
1
'#--------------------FN_DYNVARS_130_03-------------------------#'
SET @@global.query_alloc_block_size = 1024;
@ -177,8 +177,8 @@ ERROR 42S22: Unknown column 'query_alloc_block_size' in 'SELECT'
SET @@global.query_alloc_block_size = @start_global_value;
SELECT @@global.query_alloc_block_size;
@@global.query_alloc_block_size
16384
32768
SET @@session.query_alloc_block_size = @start_session_value;
SELECT @@session.query_alloc_block_size;
@@session.query_alloc_block_size
16384
32768

View file

@ -1,11 +1,11 @@
SET @start_global_value = @@global.query_prealloc_size ;
SELECT @start_global_value;
@start_global_value
24576
32768
SET @start_session_value = @@session.query_prealloc_size ;
SELECT @start_session_value;
@start_session_value
24576
32768
'#--------------------FN_DYNVARS_005_01-------------------------#'
SET @@global.query_prealloc_size = 100;
Warnings:
@ -13,22 +13,22 @@ Warning 1292 Truncated incorrect query_prealloc_size value: '100'
SET @@global.query_prealloc_size = DEFAULT;
SELECT @@global.query_prealloc_size ;
@@global.query_prealloc_size
24576
32768
SET @@session.query_prealloc_size = 200;
Warnings:
Warning 1292 Truncated incorrect query_prealloc_size value: '200'
SET @@session.query_prealloc_size = DEFAULT;
SELECT @@session.query_prealloc_size ;
@@session.query_prealloc_size
24576
32768
'#--------------------FN_DYNVARS_005_02-------------------------#'
SET @@global.query_prealloc_size = DEFAULT;
SELECT @@global.query_prealloc_size = 24576;
@@global.query_prealloc_size = 24576
SELECT @@global.query_prealloc_size = 32768;
@@global.query_prealloc_size = 32768
1
SET @@session.query_prealloc_size = DEFAULT;
SELECT @@session.query_prealloc_size = 24576;
@@session.query_prealloc_size = 24576
SELECT @@session.query_prealloc_size = 32768;
@@session.query_prealloc_size = 32768
1
'#--------------------FN_DYNVARS_005_03-------------------------#'
SET @@global.query_prealloc_size = 8192;
@ -163,8 +163,8 @@ ERROR 42S22: Unknown column 'query_prealloc_size' in 'SELECT'
SET @@global.query_prealloc_size = @start_global_value;
SELECT @@global.query_prealloc_size ;
@@global.query_prealloc_size
24576
32768
SET @@session.query_prealloc_size = @start_session_value;
SELECT @@session.query_prealloc_size ;
@@session.query_prealloc_size
24576
32768

View file

@ -223,7 +223,7 @@ DEFAULT_VALUE 268434432
VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE.
NUMERIC_MIN_VALUE 16376
NUMERIC_MIN_VALUE 16352
NUMERIC_MAX_VALUE 1152921504606846975
NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL

View file

@ -216,7 +216,7 @@ VARIABLE_NAME ARIA_SORT_BUFFER_SIZE
VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE.
NUMERIC_MIN_VALUE 16376
NUMERIC_MIN_VALUE 16352
NUMERIC_MAX_VALUE 1152921504606846975
NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL

View file

@ -216,7 +216,7 @@ VARIABLE_NAME ARIA_SORT_BUFFER_SIZE
VARIABLE_SCOPE SESSION
VARIABLE_TYPE BIGINT UNSIGNED
VARIABLE_COMMENT The buffer that is allocated when sorting the index when doing a REPAIR or when creating indexes with CREATE INDEX or ALTER TABLE.
NUMERIC_MIN_VALUE 16376
NUMERIC_MIN_VALUE 16352
NUMERIC_MAX_VALUE 1152921504606846975
NUMERIC_BLOCK_SIZE 1
ENUM_VALUE_LIST NULL

View file

@ -60,10 +60,10 @@ SELECT @@session.query_alloc_block_size;
###################################################################
SET @@global.query_alloc_block_size = DEFAULT;
SELECT @@global.query_alloc_block_size = 16384;
SELECT @@global.query_alloc_block_size = 32768;
SET @@session.query_alloc_block_size = DEFAULT;
SELECT @@session.query_alloc_block_size = 16384;
SELECT @@session.query_alloc_block_size = 32768;
--echo '#--------------------FN_DYNVARS_130_03-------------------------#'

View file

@ -60,10 +60,10 @@ SELECT @@session.query_prealloc_size ;
########################################################################
SET @@global.query_prealloc_size = DEFAULT;
SELECT @@global.query_prealloc_size = 24576;
SELECT @@global.query_prealloc_size = 32768;
SET @@session.query_prealloc_size = DEFAULT;
SELECT @@session.query_prealloc_size = 24576;
SELECT @@session.query_prealloc_size = 32768;
--echo '#--------------------FN_DYNVARS_005_03-------------------------#'

View file

@ -64,7 +64,13 @@ void init_alloc_root(PSI_memory_key key, MEM_ROOT *mem_root, size_t block_size,
mem_root->free= mem_root->used= mem_root->pre_alloc= 0;
mem_root->min_malloc= 32;
mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
/*
We remove MALLOC_OVERHEAD from blocksize to ensure that if the user
used a block size of power of 2, we will not allocate over it,
including the extra memory added by safe_malloc & malloc().
*/
mem_root->block_size= (block_size > MALLOC_OVERHEAD ?
block_size - MALLOC_OVERHEAD : 0);
mem_root->flags= 0;
if (my_flags & MY_THREAD_SPECIFIC)
mem_root->flags|= ROOT_FLAG_THREAD_SPECIFIC;
@ -114,7 +120,8 @@ void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size,
DBUG_ENTER("reset_root_defaults");
DBUG_ASSERT(alloc_root_inited(mem_root));
mem_root->block_size= block_size - ALLOC_ROOT_MIN_BLOCK_SIZE;
mem_root->block_size= (block_size > MALLOC_OVERHEAD ?
block_size - MALLOC_OVERHEAD : 0);
#if !(defined(HAVE_valgrind) && defined(EXTRA_DEBUG))
if (pre_alloc_size)
{
@ -173,7 +180,7 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
#if defined(HAVE_valgrind) && defined(EXTRA_DEBUG)
reg1 USED_MEM *next;
DBUG_ENTER("alloc_root");
DBUG_PRINT("enter",("root: %p", mem_root));
DBUG_PRINT("enter",("root: %p length: %ld", mem_root, (long) length));
DBUG_ASSERT(alloc_root_inited(mem_root));
@ -207,7 +214,7 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
reg2 USED_MEM **prev;
size_t original_length __attribute__((unused)) = length;
DBUG_ENTER("alloc_root");
DBUG_PRINT("enter",("root: %p", mem_root));
DBUG_PRINT("enter",("root: %p length: %ld", mem_root, (long) length));
DBUG_ASSERT(alloc_root_inited(mem_root));
DBUG_ASSERT((mem_root->flags & ROOT_FLAG_READ_ONLY) == 0);
@ -227,8 +234,8 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
(*prev)->left < ALLOC_MAX_BLOCK_TO_DROP)
{
next= *prev;
*prev= next->next; /* Remove block from list */
next->next= mem_root->used;
*prev= next->next; /* Remove block from free list */
next->next= mem_root->used; /* Add to used list */
mem_root->used= next;
mem_root->first_block_usage= 0;
}
@ -250,21 +257,27 @@ void *alloc_root(MEM_ROOT *mem_root, size_t length)
DBUG_RETURN((void*) 0); /* purecov: inspected */
}
mem_root->block_num++;
next->next= *prev;
next->size= get_size;
next->left= get_size-ALIGN_SIZE(sizeof(USED_MEM));
*prev=next;
next->next= 0;
DBUG_ASSERT(*prev == 0);
*prev= next; /* Put last in free list */
TRASH_MEM(next);
}
else
{
/* Reset first_block_usage if we used the first block */
if (prev == &mem_root->free)
mem_root->first_block_usage= 0;
}
point= (uchar*) ((char*) next+ (next->size-next->left));
/*TODO: next part may be unneded due to mem_root->first_block_usage counter*/
if ((next->left-= length) < mem_root->min_malloc)
{ /* Full block */
*prev= next->next; /* Remove block from list */
{
/* Full block. Move the block from the free list to the used list */
*prev= next->next;
next->next= mem_root->used;
mem_root->used= next;
mem_root->first_block_usage= 0;
}
point+= REDZONE_SIZE;
TRASH_ALLOC(point, original_length);

View file

@ -410,14 +410,14 @@ int my_load_defaults(const char *conf_file, const char **groups, int *argc,
const char **dirs;
DBUG_ENTER("my_load_defaults");
init_alloc_root(key_memory_defaults, &alloc, 512, 0, MYF(0));
init_alloc_root(key_memory_defaults, &alloc, 4096, 0, MYF(0));
if ((dirs= init_default_directories(&alloc)) == NULL)
goto err;
args_used= get_defaults_options(*argv);
if (my_init_dynamic_array(key_memory_defaults, &args, sizeof(char*), 128, 64,
MYF(0)))
if (my_init_dynamic_array(key_memory_defaults, &args, sizeof(char*),
DEFAULT_ROOT_BLOCK_SIZE, 0, MYF(0)))
goto err;
insert_dynamic(&args, *argv);/* Name MUST be set, even by embedded library */

View file

@ -803,9 +803,10 @@ THD::THD(my_thread_id id, bool is_wsrep_applier)
Pass nominal parameters to init_alloc_root only to ensure that
the destructor works OK in case of an error. The main_mem_root
will be re-initialized in init_for_queries().
The base one will mainly be use to allocate memory during authentication.
*/
init_sql_alloc(key_memory_thd_main_mem_root,
&main_mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0,
&main_mem_root, DEFAULT_ROOT_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
/*

View file

@ -3243,7 +3243,7 @@ public:
bzero((char*)this, sizeof(*this));
implicit_xid.null();
init_sql_alloc(key_memory_thd_transactions, &mem_root,
ALLOC_ROOT_MIN_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
DEFAULT_ROOT_BLOCK_SIZE, 0, MYF(MY_THREAD_SPECIFIC));
}
} default_transaction, *transaction;
Global_read_lock global_read_lock;

View file

@ -175,21 +175,26 @@
#define MYSQLD_NET_RETRY_COUNT 10 ///< Abort read after this many int.
#endif
#define QUERY_ALLOC_BLOCK_SIZE 16384
#define QUERY_ALLOC_PREALLOC_SIZE 24576
/*
Allocations with MEM_ROOT. We should try to keep these as powers of 2
and not higher than 32768 to get full benefit of allocators like
tcmalloc that will for these use a local heap without locks.
*/
#define QUERY_ALLOC_BLOCK_SIZE 32768
#define QUERY_ALLOC_PREALLOC_SIZE 32768 /* 65536 could be better */
#define TRANS_ALLOC_BLOCK_SIZE 8192
#define TRANS_ALLOC_PREALLOC_SIZE 4096
#define RANGE_ALLOC_BLOCK_SIZE 4096
#define ACL_ALLOC_BLOCK_SIZE 1024
#define UDF_ALLOC_BLOCK_SIZE 1024
#define TABLE_ALLOC_BLOCK_SIZE 1024
#define TABLE_PREALLOC_BLOCK_SIZE 8192
#define TABLE_ALLOC_BLOCK_SIZE 4096
#define WARN_ALLOC_BLOCK_SIZE 2048
#define WARN_ALLOC_PREALLOC_SIZE 1024
/*
Note that if we are using 32K or less, then TCmalloc will use a local
heap without locks!
*/
#define SHOW_ALLOC_BLOCK_SIZE (32768-MALLOC_OVERHEAD)
#define TMP_TABLE_BLOCK_SIZE 16384
#define TMP_TABLE_PREALLOC_SIZE 32768
#define SHOW_ALLOC_BLOCK_SIZE 32768
/*
The following parameters is to decide when to use an extra cache to

View file

@ -1616,9 +1616,9 @@ int plugin_init(int *argc, char **argv, int flags)
init_plugin_psi_keys();
init_alloc_root(key_memory_plugin_mem_root, &plugin_mem_root, 4096, 4096, MYF(0));
init_alloc_root(key_memory_plugin_mem_root, &plugin_vars_mem_root, 4096, 4096, MYF(0));
init_alloc_root(PSI_NOT_INSTRUMENTED, &tmp_root, 4096, 4096, MYF(0));
init_alloc_root(key_memory_plugin_mem_root, &plugin_mem_root, 4096, 16384, MYF(0));
init_alloc_root(key_memory_plugin_mem_root, &plugin_vars_mem_root, 4096, 32768, MYF(0));
init_alloc_root(PSI_NOT_INSTRUMENTED, &tmp_root, 16384, 32768, MYF(0));
if (my_hash_init(key_memory_plugin_bookmark, &bookmark_hash, &my_charset_bin, 32, 0, 0,
get_bookmark_hash_key, NULL, HASH_UNIQUE))

View file

@ -20049,8 +20049,8 @@ TABLE *Create_tmp_table::start(THD *thd,
copy_func_count+= param->sum_func_count;
param->copy_func_count= copy_func_count;
init_sql_alloc(key_memory_TABLE, &own_root, TABLE_ALLOC_BLOCK_SIZE, 0,
MYF(MY_THREAD_SPECIFIC));
init_sql_alloc(key_memory_TABLE, &own_root, TMP_TABLE_BLOCK_SIZE,
TMP_TABLE_PREALLOC_SIZE, MYF(MY_THREAD_SPECIFIC));
if (!multi_alloc_root(&own_root,
&table, sizeof(*table),

View file

@ -345,8 +345,8 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name,
path_length= build_table_filename(path, sizeof(path) - 1,
db, table_name, "", 0);
init_sql_alloc(key_memory_table_share, &mem_root, TABLE_ALLOC_BLOCK_SIZE, 0,
MYF(0));
init_sql_alloc(key_memory_table_share, &mem_root, TABLE_ALLOC_BLOCK_SIZE,
TABLE_PREALLOC_BLOCK_SIZE, MYF(0));
if (multi_alloc_root(&mem_root,
&share, sizeof(*share),
&key_buff, key_length,
@ -435,10 +435,12 @@ void init_tmp_table_share(THD *thd, TABLE_SHARE *share, const char *key,
bzero((char*) share, sizeof(*share));
/*
This can't be MY_THREAD_SPECIFIC for slaves as they are freed
during cleanup() from Relay_log_info::close_temporary_tables()
during cleanup() from Relay_log_info::close_temporary_tables().
We can also not use pre-alloc here, as internal temporary tables
are not freeing table->share->mem_root
*/
init_sql_alloc(key_memory_table_share, &share->mem_root,
TABLE_ALLOC_BLOCK_SIZE, 0,
TABLE_PREALLOC_BLOCK_SIZE, 0,
MYF(thd->slave_thread ? 0 : MY_THREAD_SPECIFIC));
share->table_category= TABLE_CATEGORY_TEMPORARY;
share->tmp_table= INTERNAL_TMP_TABLE;
@ -4136,7 +4138,7 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share,
goto err;
}
init_sql_alloc(key_memory_TABLE, &outparam->mem_root, TABLE_ALLOC_BLOCK_SIZE,
0, MYF(0));
TABLE_PREALLOC_BLOCK_SIZE, MYF(0));
/*
We have to store the original alias in mem_root as constraints and virtual
@ -4512,8 +4514,8 @@ partititon_err:
thd->lex->context_analysis_only= save_context_analysis_only;
DBUG_EXECUTE_IF("print_long_unique_internal_state",
print_long_unique_table(outparam););
DBUG_RETURN (OPEN_FRM_OK);
print_long_unique_table(outparam););
DBUG_RETURN(OPEN_FRM_OK);
err:
if (! error_reported)