Skip to content

Commit ab84b8e

Browse files
abychkopolchawa-percona
authored andcommitted
PS-10595 [8.4]: Add innodb_large_page_populate to control huge page pre-population
https://perconadev.atlassian.net/browse/PS-10595 Introduce a new InnoDB boolean system variable innodb_large_page_populate (Linux only) that controls whether huge pages and page-aligned allocations used by the buffer pool and the page allocator are pre-populated at allocation time. When OFF (default), large_page_aligned_alloc() and page_aligned_alloc() skip MAP_POPULATE and the explicit prefault step by masking the caller's populate request with the global flag. This reduces startup time and NUMA-related overhead on large systems with huge pages. When ON, the previous behavior (honor the caller's populate request) is preserved. The backing global is named srv_large_page_populate, following the srv_ naming convention used by other InnoDB globals. On non-Linux platforms the flag is a compile-time constant true, so behavior there is unchanged.
1 parent a1ad3ef commit ab84b8e

5 files changed

Lines changed: 41 additions & 4 deletions

File tree

storage/innobase/handler/ha_innodb.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24162,6 +24162,15 @@ static MYSQL_SYSVAR_BOOL(
2416224162
nullptr, nullptr, true);
2416324163
#endif /* HAVE_LIBNUMA */
2416424164

24165+
#ifdef UNIV_LINUX
24166+
static MYSQL_SYSVAR_BOOL(
24167+
large_page_populate, srv_large_page_populate, PLUGIN_VAR_NOCMDARG,
24168+
"Pre-populate huge / large pages used by the InnoDB buffer pool and page "
24169+
"allocator at allocation time. When OFF (default), population is skipped "
24170+
"to reduce startup time and NUMA overhead on large systems.",
24171+
nullptr, nullptr, false);
24172+
#endif /* UNIV_LINUX */
24173+
2416524174
static MYSQL_SYSVAR_BOOL(
2416624175
api_enable_binlog, ib_binlog_enabled,
2416724176
PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
@@ -24620,6 +24629,9 @@ static SYS_VAR *innobase_system_variables[] = {
2462024629
#ifdef HAVE_LIBNUMA
2462124630
MYSQL_SYSVAR(numa_interleave),
2462224631
#endif /* HAVE_LIBNUMA */
24632+
#ifdef UNIV_LINUX
24633+
MYSQL_SYSVAR(large_page_populate),
24634+
#endif /* UNIV_LINUX */
2462324635
MYSQL_SYSVAR(change_buffering),
2462424636
MYSQL_SYSVAR(change_buffer_max_size),
2462524637
#if defined UNIV_DEBUG || defined UNIV_IBUF_DEBUG

storage/innobase/include/detail/ut/large_page_alloc.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ this program; if not, write to the Free Software Foundation, Inc.,
4848
#include "storage/innobase/include/detail/ut/helper.h"
4949
#include "storage/innobase/include/detail/ut/page_metadata.h"
5050
#include "storage/innobase/include/detail/ut/pfs.h"
51+
#include "storage/innobase/include/os0populate.h"
5152

5253
extern const size_t large_page_default_size;
5354

@@ -112,7 +113,8 @@ struct Large_page_alloc : public allocator_traits<false> {
112113
static inline void *alloc(std::size_t size, bool populate) {
113114
auto total_len = round_to_next_multiple(
114115
size + page_allocation_metadata::len, large_page_default_size);
115-
auto mem = large_page_aligned_alloc(total_len, populate);
116+
bool do_populate = populate && srv_large_page_populate;
117+
auto mem = large_page_aligned_alloc(total_len, do_populate);
116118
if (unlikely(!mem)) return nullptr;
117119
page_allocation_metadata::datalen(mem, total_len);
118120
page_allocation_metadata::page_type(mem, Page_type::large_page);
@@ -249,7 +251,8 @@ struct Large_page_alloc_pfs : public allocator_traits<true> {
249251
page_allocation_metadata::pfs_metadata::pfs_memory_key_t key) {
250252
auto total_len = round_to_next_multiple(
251253
size + page_allocation_metadata::len, large_page_default_size);
252-
auto mem = large_page_aligned_alloc(total_len, populate);
254+
bool do_populate = populate && srv_large_page_populate;
255+
auto mem = large_page_aligned_alloc(total_len, do_populate);
253256
if (unlikely(!mem)) return nullptr;
254257

255258
#ifdef HAVE_PSI_MEMORY_INTERFACE

storage/innobase/include/detail/ut/page_alloc.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ namespace detail {
6666
@return Pointer to the allocated storage. nullptr if allocation failed.
6767
*/
6868
inline void *page_aligned_alloc(size_t n_bytes, bool populate) {
69+
const bool do_populate = populate && srv_large_page_populate;
6970
#ifdef _WIN32
7071
// With lpAddress set to nullptr, VirtualAlloc will internally round n_bytes
7172
// to the multiple of system page size if it is not already
@@ -83,7 +84,7 @@ inline void *page_aligned_alloc(size_t n_bytes, bool populate) {
8384
// multiple of system page size if it is not already
8485
void *ptr =
8586
mmap(nullptr, n_bytes, PROT_READ | PROT_WRITE,
86-
MAP_PRIVATE | MAP_ANON | (populate ? OS_MAP_POPULATE : 0), -1, 0);
87+
MAP_PRIVATE | MAP_ANON | (do_populate ? OS_MAP_POPULATE : 0), -1, 0);
8788
if (unlikely(ptr == (void *)-1)) {
8889
ib::log_warn(ER_IB_MSG_856) << "page_aligned_alloc mmap(" << n_bytes
8990
<< " bytes) failed;"
@@ -93,7 +94,7 @@ inline void *page_aligned_alloc(size_t n_bytes, bool populate) {
9394
}
9495
#endif
9596

96-
if (populate) prefault_if_not_map_populate(ptr, n_bytes);
97+
if (do_populate) prefault_if_not_map_populate(ptr, n_bytes);
9798

9899
return ptr;
99100
}

storage/innobase/include/os0populate.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,18 @@
1717
void prefault_if_not_map_populate(void *ptr [[maybe_unused]],
1818
size_t n_bytes [[maybe_unused]]);
1919

20+
/* UNIV_LINUX is supplied on the compiler command line (see innodb.cmake), so it
21+
is available here even though this header must not include univ.i. */
22+
#ifdef UNIV_LINUX
23+
/** When false, huge-page / page-aligned allocations skip pre-population
24+
(MAP_POPULATE and the explicit prefault step) even when the caller requests it.
25+
Backed by the innodb_large_page_populate system variable. */
26+
extern bool srv_large_page_populate;
27+
#else
28+
/* On non-Linux platforms population is always honored as requested. constexpr
29+
gives this internal linkage, so each translation unit gets its own copy and
30+
there is no multiple-definition problem. */
31+
constexpr bool srv_large_page_populate = true;
32+
#endif
33+
2034
#endif

storage/innobase/srv/srv0srv.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,13 @@ bool srv_use_native_aio = false;
235235

236236
bool srv_numa_interleave = false;
237237

238+
#ifdef UNIV_LINUX
239+
/** Whether huge / page-aligned allocations honor a caller's populate request.
240+
Exposed as the innodb_large_page_populate system variable. Default off: skip
241+
pre-population to reduce startup time and NUMA overhead on large systems. */
242+
bool srv_large_page_populate = false;
243+
#endif /* UNIV_LINUX */
244+
238245
#ifdef UNIV_DEBUG
239246
/** Force all user tables to use page compression. */
240247
ulong srv_debug_compress;

0 commit comments

Comments
 (0)