Reduce glibc memory fragmentation (#19385)
* reorder memory charts * make aral use mmap only * onewayalloc uses mmap * faster growth for onewayalloc
Costa Tsaousis committed
Jan 12, 2025 at 23:24 UTC
3f172d7923bd32ebfc39d9b3c505d7363b21b7d3
3 files changed
+73
-56
src/daemon/pulse/pulse-daemon-memory.c
+34
-34
@@ -180,39 +180,6 @@ void pulse_daemon_memory_do(bool extended) {
180
181
// ----------------------------------------------------------------------------------------------------------------
182
183
- OS_SYSTEM_MEMORY sm = os_system_memory(true);
184
- if (sm.ram_total_bytes && dbengine_out_of_memory_protection) {
185
- static RRDSET *st_memory_available = NULL;
186
- static RRDDIM *rd_available = NULL;
187
-
188
- if (unlikely(!st_memory_available)) {
189
- st_memory_available = rrdset_create_localhost(
190
- "netdata",
191
- "out_of_memory_protection",
192
- NULL,
193
- "Memory Usage",
194
- NULL,
195
- "Out of Memory Protection",
196
- "bytes",
197
- "netdata",
198
- "pulse",
199
- 130102,
200
- localhost->rrd_update_every,
201
- RRDSET_TYPE_AREA);
202
-
203
- rd_available = rrddim_add(st_memory_available, "available", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
204
- }
205
-
206
- // the sum of all these needs to be above at the total buffers calculation
207
- rrddim_set_by_pointer(
208
- st_memory_available, rd_available,
209
- (collected_number)sm.ram_available_bytes);
210
-
211
- rrdset_done(st_memory_available);
212
- }
213
-
214
- // ----------------------------------------------------------------------------------------------------------------
215
-
183
{
184
static RRDSET *st_memory_buffers = NULL;
185
static RRDDIM *rd_queries = NULL;
@@ -241,7 +208,7 @@ void pulse_daemon_memory_do(bool extended) {
208
"bytes",
209
"netdata",
210
"pulse",
244
- 130103,
211
+ 130102,
212
localhost->rrd_update_every,
213
RRDSET_TYPE_STACKED);
214
@@ -282,5 +249,38 @@ void pulse_daemon_memory_do(bool extended) {
249
250
// ----------------------------------------------------------------------------------------------------------------
251
252
+ OS_SYSTEM_MEMORY sm = os_system_memory(true);
253
+ if (sm.ram_total_bytes && dbengine_out_of_memory_protection) {
254
+ static RRDSET *st_memory_available = NULL;
255
+ static RRDDIM *rd_available = NULL;
256
+
257
+ if (unlikely(!st_memory_available)) {
258
+ st_memory_available = rrdset_create_localhost(
259
+ "netdata",
260
+ "out_of_memory_protection",
261
+ NULL,
262
+ "Memory Usage",
263
+ NULL,
264
+ "Out of Memory Protection",
265
+ "bytes",
266
+ "netdata",
267
+ "pulse",
268
+ 130103,
269
+ localhost->rrd_update_every,
270
+ RRDSET_TYPE_AREA);
271
+
272
+ rd_available = rrddim_add(st_memory_available, "available", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
273
+ }
274
+
275
+ // the sum of all these needs to be above at the total buffers calculation
276
+ rrddim_set_by_pointer(
277
+ st_memory_available, rd_available,
278
+ (collected_number)sm.ram_available_bytes);
279
+
280
+ rrdset_done(st_memory_available);
281
+ }
282
+
283
+ // ----------------------------------------------------------------------------------------------------------------
284
+
285
pulse_daemon_memory_system_do(extended);
286
}
src/libnetdata/aral/aral.c
+10
-3
@@ -27,7 +27,10 @@
27
28
// in malloc mode, when the page is bigger than this
29
// use anonymous private mmap pages
30
-#define ARAL_MMAP_PAGES_ABOVE (32ULL * 1024)
30
+#define ARAL_MALLOC_USE_MMAP_ABOVE (4096ULL * 4)
31
+
32
+// do not allocate pages smaller than this
33
+#define ARAL_MIN_PAGE_SIZE (4096ULL * 4)
34
35
#define ARAL_PAGE_INCOMING_PARTITIONS 4 // up to 32 (32-bits bitmap)
36
@@ -493,7 +496,7 @@ static size_t aral_next_allocation_size___adders_lock_needed(ARAL *ar, bool mark
496
ar->ops[idx].adders.allocation_size = size;
497
}
498
496
- if(!ar->config.mmap.enabled && size < ARAL_MMAP_PAGES_ABOVE) {
499
+ if(!ar->config.mmap.enabled && size < ARAL_MALLOC_USE_MMAP_ABOVE) {
500
// when doing malloc, don't allocate entire pages, but only what needed
501
size =
502
aral_elements_in_page_size(ar, size) * ar->config.element_size +
@@ -542,7 +545,7 @@ static ARAL_PAGE *aral_create_page___no_lock_needed(ARAL *ar, size_t size TRACE_
545
else {
546
size_t ARAL_PAGE_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT);
547
545
- if (size >= ARAL_MMAP_PAGES_ABOVE) {
548
+ if (size >= ARAL_MALLOC_USE_MMAP_ABOVE) {
549
bool mapped;
550
uint8_t *ptr = netdata_mmap(NULL, size, MAP_PRIVATE, 1, false, ar->config.options & ARAL_DONT_DUMP, NULL);
551
if (ptr) {
@@ -1104,6 +1107,10 @@ ARAL *aral_create(const char *name, size_t element_size, size_t initial_page_ele
1107
1108
// find the minimum page size we will use
1109
size_t min_required_page_size = memory_alignment(sizeof(ARAL_PAGE), SYSTEM_REQUIRED_ALIGNMENT) + 2 * ar->config.element_size;
1110
+
1111
+ if(min_required_page_size < ARAL_MIN_PAGE_SIZE)
1112
+ min_required_page_size = ARAL_MIN_PAGE_SIZE;
1113
+
1114
min_required_page_size = memory_alignment(min_required_page_size, ar->config.system_page_size);
1115
1116
// make sure the maximum is enough
src/libnetdata/onewayalloc/onewayalloc.c
+29
-19
@@ -8,10 +8,10 @@ typedef struct owa_page {
8
size_t stats_pages_size;
9
size_t stats_mallocs_made;
10
size_t stats_mallocs_size;
11
- size_t size; // the total size of the page
12
- size_t offset; // the first free byte of the page
13
- struct owa_page *next; // the next page on the list
14
- struct owa_page *last; // the last page on the list - we currently allocate on this
11
+ size_t size; // the total size of the page
12
+ size_t offset; // the first free byte of the page
13
+ struct owa_page *next; // the next page on the list
14
+ struct owa_page *last; // the last page on the list - we currently allocate on this
15
} OWA_PAGE;
16
17
static size_t onewayalloc_total_memory = 0;
@@ -30,7 +30,7 @@ static inline size_t natural_alignment(size_t size) {
30
}
31
32
// Create an OWA
33
-// Once it is created, the called may call the onewayalloc_mallocz()
33
+// Once it is created, the caller may call the onewayalloc_mallocz()
34
// any number of times, for any amount of memory.
35
36
static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
@@ -45,34 +45,44 @@ static OWA_PAGE *onewayalloc_create_internal(OWA_PAGE *head, size_t size_hint) {
45
}
46
47
// our default page size
48
- size_t size = OWA_NATURAL_PAGE_SIZE;
48
+ size_t size = 32768;
49
50
// make sure the new page will fit both the requested size
51
// and the OWA_PAGE structure at its beginning
52
size_hint += natural_alignment(sizeof(OWA_PAGE));
53
54
// prefer the user size if it is bigger than our size
55
- if(size_hint > size) size = size_hint;
55
+ if(size_hint > size)
56
+ size = size_hint;
57
57
- // try to allocate half of the total we have allocated already
58
- if(likely(head)) {
59
- size_t optimal_size = head->stats_pages_size / 2;
60
- if(optimal_size > size) size = optimal_size;
58
+ if(head) {
59
+ // double the current allocation
60
+ size_t optimal_size = head->stats_pages_size;
61
+
62
+ // cap it at 1 MiB
63
+ if(optimal_size > 1ULL * 1024 * 1024)
64
+ optimal_size = 1ULL * 1024 * 1024;
65
+
66
+ // use the optimal if it is more than the required size
67
+ if(optimal_size > size)
68
+ size = optimal_size;
69
}
70
71
// Make sure our allocations are always a multiple of the hardware page size
64
- if(size % OWA_NATURAL_PAGE_SIZE) size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
72
+ if(size % OWA_NATURAL_PAGE_SIZE)
73
+ size = size + OWA_NATURAL_PAGE_SIZE - (size % OWA_NATURAL_PAGE_SIZE);
74
+
75
+ // Use netdata_mmap instead of mallocz
76
+ OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0, false, false, NULL);
77
+ if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
78
66
- // OWA_PAGE *page = (OWA_PAGE *)netdata_mmap(NULL, size, MAP_ANONYMOUS|MAP_PRIVATE, 0);
67
- // if(unlikely(!page)) fatal("Cannot allocate onewayalloc buffer of size %zu", size);
68
- OWA_PAGE *page = (OWA_PAGE *)mallocz(size);
79
__atomic_add_fetch(&onewayalloc_total_memory, size, __ATOMIC_RELAXED);
80
81
page->size = size;
82
page->offset = natural_alignment(sizeof(OWA_PAGE));
83
page->next = page->last = NULL;
84
75
- if(unlikely(!head)) {
85
+ if(!head) {
86
// this is the first time we are called
87
head = page;
88
head->stats_pages = 0;
@@ -205,9 +215,9 @@ void onewayalloc_destroy(ONEWAYALLOC *owa) {
215
OWA_PAGE *p = page;
216
page = page->next;
217
208
- // munmap(p, p->size);
209
- freez(p);
218
+ // Use netdata_munmap instead of freez
219
+ netdata_munmap(p, p->size);
220
}
221
222
__atomic_sub_fetch(&onewayalloc_total_memory, total_size, __ATOMIC_RELAXED);
213
-}
223
+}
\ No newline at end of file