malloc rarely benefits from Transparent Huge Page (THP)

Fengkai Sun qcloud1014@gmail.com
Tue Jul 9 06:31:44 GMT 2024


Hello Adhemerval,

Thank you very much for your reply! I did not know about glibc's and
malloc's tunables before reading your reply.

I read that `glibc.malloc.hugetlb` was introduced in glibc 2.35. Does that
mean prior to 2.35, malloc just doesn't back small allocations to huge
pages?

By the way, I write a simple script to check how different versions of
malloc works with huge pages:

```
# generator.py
import sys

bit = sys.argv[1]

s = f"""#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char buf[65536];

int main()
{{
    for (int i = 0; i < ALLOC_1G / (1 << {bit}); i++) {{
        void *p = malloc(1 << {bit});
        memset(p, 0, 1 << {bit});
    }}

    FILE *f = fopen("/proc/self/smaps_rollup", "r");
    size_t s = fread(buf, 1, 65536, f);
    for (unsigned i = 0; i < s; i++)
        printf("%c", buf[i]);

    return 0;
}}
"""

out_path = "./main_interleave.c"
with open(out_path, "w") as f:
    f.write(s)
```

```
#!/bin/bash

# run.sh

for (( i = 4;i<30;i++ ));
do
    python generator.py $i
    # using glibc 2.39
    gcc -o main_interleave main_interleave.c
-Wl,--dynamic-linker,/home/iom/glibc-iom/build/glibc-O2/lib/ld-linux-x86-64.so.2
    output=$(GLIBC_TUNABLES=glibc.malloc.hugetlb=1
LD_LIBRARY_PATH=/home/iom/glibc-iom/build/glibc-O2/lib ./main_interleave)
    echo "Allocation: 1 << $i, total: 1 << 30"
    echo "$output" | sed -E '/^Private_Dirty:/!d'
    echo "$output" | sed -E '/^AnonHugePages:/!d'
    echo "------"
done
```

`run.sh` gives the following output:

Using glibc 2.31, THP `always` mode: before allocation size reaches 1 <<
21, `AnonHugePages` is 0, then:
Allocation: 1 << 21, total: 1 << 30
Private_Dirty:   1050720 kB
AnonHugePages:      4096 kB
------
Allocation: 1 << 22, total: 1 << 30
Private_Dirty:   1049692 kB
AnonHugePages:    524288 kB
------
Allocation: 1 << 23, total: 1 << 30
Private_Dirty:   1049184 kB
AnonHugePages:    790528 kB
------
Allocation: 1 << 24, total: 1 << 30
Private_Dirty:   1048924 kB
AnonHugePages:    921600 kB
------
Allocation: 1 << 25, total: 1 << 30
Private_Dirty:   1048796 kB
AnonHugePages:    983040 kB
------
Allocation: 1 << 26, total: 1 << 30
Private_Dirty:   1048732 kB
AnonHugePages:   1015808 kB
------
Allocation: 1 << 27, total: 1 << 30
Private_Dirty:   1048704 kB
AnonHugePages:   1032192 kB
------
Allocation: 1 << 28, total: 1 << 30
Private_Dirty:   1048688 kB
AnonHugePages:   1040384 kB
------
Allocation: 1 << 29, total: 1 << 30
Private_Dirty:   1048680 kB
AnonHugePages:   1044480 kB

Using glibc 2.31, THP `madvise` mode: `AnonHugePages` always 0.

Using glibc 2.39, THP `always` mode, glibc.malloc.hugetlb=1:  the same as
glibc 2.31, THP `always` mode.

Using glibc 2.39, THP `madvise` mode, glibc.malloc.hugetlb=1:

Allocation: 1 << 4, total: 1 << 30
Private_Dirty:   2098688 kB
AnonHugePages:   2095104 kB
------
Allocation: 1 << 5, total: 1 << 30
Private_Dirty:   1574144 kB
AnonHugePages:   1570816 kB
------
Allocation: 1 << 6, total: 1 << 30
Private_Dirty:   1312664 kB
AnonHugePages:   1308672 kB
------
Allocation: 1 << 7, total: 1 << 30
Private_Dirty:   1180460 kB
AnonHugePages:   1177600 kB
------
Allocation: 1 << 8, total: 1 << 30
Private_Dirty:   1116104 kB
AnonHugePages:   1112064 kB
------
Allocation: 1 << 9, total: 1 << 30
Private_Dirty:   1082276 kB
AnonHugePages:   1079296 kB
------
Allocation: 1 << 10, total: 1 << 30
Private_Dirty:   1066292 kB
AnonHugePages:   1062912 kB
------
Allocation: 1 << 11, total: 1 << 30
Private_Dirty:   1058388 kB
AnonHugePages:   1054720 kB
------
Allocation: 1 << 12, total: 1 << 30
Private_Dirty:   1052956 kB
AnonHugePages:   1050624 kB
------
Allocation: 1 << 13, total: 1 << 30
Private_Dirty:   1051116 kB
AnonHugePages:   1048576 kB
------
Allocation: 1 << 14, total: 1 << 30
Private_Dirty:   1051528 kB
AnonHugePages:   1048576 kB
------
Allocation: 1 << 15, total: 1 << 30
Private_Dirty:   1050148 kB
AnonHugePages:   1046528 kB
------
Allocation: 1 << 16, total: 1 << 30
Private_Dirty:   1049256 kB
AnonHugePages:   1046528 kB
------
Allocation: 1 << 17, total: 1 << 30
Private_Dirty:   1081392 kB
AnonHugePages:         0 kB
------
Allocation: 1 << 18, total: 1 << 30
Private_Dirty:   1065040 kB
AnonHugePages:         0 kB
------
Allocation: 1 << 19, total: 1 << 30
Private_Dirty:   1056868 kB
AnonHugePages:         0 kB
------
Allocation: 1 << 20, total: 1 << 30
Private_Dirty:   1052776 kB
AnonHugePages:         0 kB
------
Allocation: 1 << 21, total: 1 << 30
Private_Dirty:   1050736 kB
AnonHugePages:      4096 kB
------
Allocation: 1 << 22, total: 1 << 30
Private_Dirty:   1049712 kB
AnonHugePages:    528384 kB
------
Allocation: 1 << 23, total: 1 << 30
Private_Dirty:   1049200 kB
AnonHugePages:    786432 kB
------
Allocation: 1 << 24, total: 1 << 30
Private_Dirty:   1048940 kB
AnonHugePages:    917504 kB
------
Allocation: 1 << 25, total: 1 << 30
Private_Dirty:   1048816 kB
AnonHugePages:    983040 kB
------
Allocation: 1 << 26, total: 1 << 30
Private_Dirty:   1048748 kB
AnonHugePages:   1015808 kB
------
Allocation: 1 << 27, total: 1 << 30
Private_Dirty:   1048716 kB
AnonHugePages:   1032192 kB
------
Allocation: 1 << 28, total: 1 << 30
Private_Dirty:   1048700 kB
AnonHugePages:   1040384 kB
------
Allocation: 1 << 29, total: 1 << 30
Private_Dirty:   1048696 kB
AnonHugePages:   1044480 kB

I'm wondering why the memory backed to huge pages suddenly drops to 0 when
single allocation is 1 << 17 bytes.


Also, I found that even I use glibc 2.39, THP madvise mode, and
glibc.malloc.hugetlb=1, complex applications still does not benefit much
from THP (10M backed to THP even if it consumes ~500M VM). Do you know the
possible reason for this problem or some other tunables can help?

---
Thanks,
Fengkai

On Mon, Jul 8, 2024 at 5:02 AM Adhemerval Zanella Netto <
adhemerval.zanella@linaro.org> wrote:

>
>
> On 07/07/24 06:26, Fengkai Sun via Libc-help wrote:
> > Hi list,
> >
> > I'm expecting to see some speed up or at least reduction of dTLB misses
> of
> > a malloc-intensive program, after enabling THP on my system. However,
> > things do not quite go as expected.
> >
> > Consider the program below:
> >
> > ```
> > #include <stdio.h>
> > #include <stdlib.h>
> > #include <string.h>
> >
> > #define SEGMENT_1K (1 << 10)
> > #define SEGMENT_4K (1 << 12)
> > #define SEGMENT_8K (1 << 13)
> > #define SEGMENT_1M (1 << 20)
> > #define SEGMENT_2M (1 << 21)
> > #define SEGMENT_4M (1 << 22)
> > #define ALLOC_32M (1 << 25)
> > #define ALLOC_1G (1 << 30)
> >
> > int main()
> > {
> >     for (int i = 0; i < ALLOC_1G / SEGMENT_1K; i++) {
> >         void *p = malloc(SEGMENT_1K);
> >         memset(p, 0, SEGMENT_1K);
> >     }
> >
> >     // used to check memory mappings from another shell
> >     int sleeper;
> >     scanf("%d", &sleeper);
> >
> >     return 0;
> > }
> > ```
> >
> > `AnonHugePages` in `/proc/pid/smaps_rollup` indicates how much memory is
> > backed by THP in this process.
> >
> > Results:
> > Using SEGMENT_1K, 4K, 8K, 1M has AnonHugePages == 0;
> > Using SEGMENT_2M has AnonHugePages == 4096 kB;
> > Using SEGMENT_4M has AnonHugePages == 524288 kB.
> >
> > I believe this means malloc can only benefit from THP if a single
> > allocation is at least over 2M.
> > However, I don't think there would be much realistic workload doing that.
> > I've run a few quite complex applications at hand and found very few
> pages
> > backed to THP.
> >
> > I think it's a little counter-intuitive that the kernel (and/or malloc)
> > does not back small allocations to THP. Please tell me if I missed
> > something or this is an expected behavior.
> >
> >
> > I'm using glibc 2.31 under Ubuntu 20.04.1, kernel 5.15.0-113-generic.
> > Both `/sys/kernel/mm/transparent_hugepage/enabled` and
> > `/sys/kernel/mm/transparent_hugepage/defrag` are set to `always`.
>
> Did you try to use GLIBC_TUNABLES=glibc.malloc.hugetlb=1? I don't recall
> exactly if it would improve THP usage on 'always' mode, but it instruct
> malloc aligned brk allocation to the THP size (madvise_thp) and issue a
> madvise with (MADV_HUGEPAGE) to help kernel realize the allocation should
> be backed by HugePages.  It also help on arena allocation by aligning
> the initial mmap.
>
>


More information about the Libc-help mailing list