요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
============================
Transparent Hugepage Support
============================
Objective
=========
Performance critical computing applications dealing with large memory
working sets are already running on top of libhugetlbfs and in turn
hugetlbfs. Transparent HugePage Support (THP) is an alternative mean of
using huge pages for the backing of virtual memory with huge pages
that supports the automatic promotion and demotion of page sizes and
without the shortcomings of hugetlbfs.
Currently THP only works for anonymous memory mappings and tmpfs/shmem.
But in the future it can expand to other filesystems.
.. note::
in the examples below we presume that the basic page size is 4K and
the huge page size is 2M, although the actual numbers may vary
depending on the CPU architecture.
The reason applications are running faster is because of two
factors. The first factor is almost completely irrelevant and it's not
of significant interest because it'll also have the downside of
requiring larger clear-page copy-page in page faults which is a
potentially negative effect. The first factor consists in taking a
single page fault for each 2M virtual region touched by userland (so
reducing the enter/exit kernel frequency by a 512 times factor). This
only matters the first time the memory is accessed for the lifetime of
a memory mapping. The second long lasting and much more important
factor will affect all subsequent accesses to the memory for the whole
runtime of the application. The second factor consist of two
components:
1) the TLB miss will run faster (especially with virtualization using
nested pagetables but almost always also on bare metal without
virtualization)
2) a single TLB entry will be mapping a much larger amount of virtual
memory in turn reducing the number of TLB misses. With
virtualization and nested pagetables the TLB can be mapped of
larger size only if both KVM and the Linux guest are using
hugepages but a significant speedup already happens if only one of
the two is using hugepages just because of the fact the TLB miss is
going to run faster.
Modern kernels support "multi-size THP" (mTHP), which introduces the
ability to allocate memory in blocks that are bigger than a base page
but smaller than traditional PMD-size (as described above), in
increments of a power-of-2 number of pages. mTHP can back anonymous
memory (for example 16K, 32K, 64K, etc). These THPs continue to be
PTE-mapped, but in many cases can still provide similar benefits to
those outlined above: Page faults are significantly reduced (by a
factor of e.g. 4, 8, 16, etc), but latency spikes are much less
prominent because the size of each page isn't as huge as the PMD-sized
variant and there is less memory to clear in each page fault. Some
architectures also employ TLB compression mechanisms to squeeze more
entries in when a set of PTEs are virtually and physically contiguous
and approporiately aligned. In this case, TLB misses will occur less
often.
THP can be enabled system wide or restricted to certain tasks or even
memory ranges inside task's address space. Unless THP is completely
disabled, there is ``khugepaged`` daemon that scans memory and
collapses sequences of basic pages into PMD-sized huge pages.
The THP behaviour is controlled via :ref:`sysfs <thp_sysfs>`
interface and using madvise(2) and prctl(2) system calls.
Transparent Hugepage Support maximizes the usefulness of free memory
if compared to the reservation approach of hugetlbfs by allowing all
unused memory to be used as cache or other movable (or even unmovable
entities). It doesn't require reservation to prevent hugepage
allocation failures to be noticeable from userland. It allows paging
and all other advanced VM features to be available on the
hugepages. It requires no modifications for applications to take
advantage of it.
Applications however can be further optimized to take advantage of
this feature, like for example they've been optimized before to avoid
a flood of mmap system calls for every malloc(4k). Optimizing userland
is by far not mandatory and khugepaged already can take care of long
lived page allocations even for hugepage unaware applications that
deals with large amounts of memory.
In certain cases when hugepages are enabled system wide, application
may end up allocating more memory resources. An application may mmap a
large region but only touch 1 byte of it, in that case a 2M page might
be allocated instead of a 4k page for no good. This is why it's
possible to disable hugepages system-wide and to only have them inside
MADV_HUGEPAGE madvise regions.
Embedded systems should enable hugepages only inside madvise regions
to eliminate any risk of wasting any precious byte of memory and to
only run faster.
Applications that gets a lot of benefit from hugepages and that don't
risk to lose memory by using hugepages, should use
madvise(MADV_HUGEPAGE) on their critical mmapped regions.
.. _thp_sysfs:
sysfs
=====
Global THP controls
-------------------
Transparent Hugepage Support for anonymous memory can be disabled
(mostly for debugging purposes) or only enabled inside MADV_HUGEPAGE
regions (to avoid the risk of consuming more memory resources) or enabled
system wide. This can be achieved per-supported-THP-size with one of::
echo always >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
echo never >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
where <size> is the hugepage size being addressed, the available sizes
for which vary by system.
.. note:: Setting "never" in all sysfs THP controls does **not** disable
Transparent Huge Pages globally. This is because ``madvise(...,
MADV_COLLAPSE)`` ignores these settings and collapses ranges to
PMD-sized huge pages unconditionally.
For example::
echo always >/sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled
Alternatively it is possible to specify that a given hugepage size
will inherit the top-level "enabled" value::
echo inherit >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
For example::
echo inherit >/sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled
The top-level setting (for use with "inherit") can be set by issuing
one of the following commands::
echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled
By default, PMD-sized hugepages have enabled="inherit" and all other
hugepage sizes have enabled="never". If enabling multiple hugepage
sizes, the kernel will select the most appropriate enabled size for a
given allocation.
It's also possible to limit defrag efforts in the VM to generate
anonymous hugepages in case they're not immediately free to madvise
regions or to never try to defrag memory and simply fallback to regular
pages unless hugepages are immediately available. Clearly if we spend CPU
time to defrag memory, we would expect to gain even more by the fact we
use hugepages later instead of regular pages. This isn't always
guaranteed, but it may be more likely in case the allocation is for a
MADV_HUGEPAGE region.
::
echo always >/sys/kernel/mm/transparent_hugepage/defrag
echo defer >/sys/kernel/mm/transparent_hugepage/defrag
echo defer+madvise >/sys/kernel/mm/transparent_hugepage/defrag
echo madvise >/sys/kernel/mm/transparent_hugepage/defrag
echo never >/sys/kernel/mm/transparent_hugepage/defrag
always
means that an application requesting THP will stall on
allocation failure and directly reclaim pages and compact
memory in an effort to allocate a THP immediately. This may be
desirable for virtual machines that benefit heavily from THP
use and are willing to delay the VM start to utilise them.
defer
means that an application will wake kswapd in the background
to reclaim pages and wake kcompactd to compact memory so that
THP is available in the near future. It's the responsibility
of khugepaged to then install the THP pages later.
defer+madvise
will enter direct reclaim and compaction like ``always``, but
only for regions that have used madvise(MADV_HUGEPAGE); all
other regions will wake kswapd in the background to reclaim
pages and wake kcompactd to compact memory so that THP is
available in the near future.
madvise
will enter direct reclaim like ``always`` but only for regions
that are have used madvise(MADV_HUGEPAGE). This is the default
behaviour.
never
should be self-explanatory. Note that ``madvise(...,
MADV_COLLAPSE)`` can still cause transparent huge pages to be
obtained even if this mode is specified everywhere.
By default kernel tries to use huge, PMD-mappable zero page on read
page fault to anonymous mapping. It's possible to disable huge zero
page by writing 0 or enable it back by writing 1::
echo 0 >/sys/kernel/mm/transparent_hugepage/use_zero_page
echo 1 >/sys/kernel/mm/transparent_hugepage/use_zero_page
Some userspace (such as a test program, or an optimized memory
allocation library) may want to know the size (in bytes) of a
PMD-mappable transparent hugepage::
cat /sys/kernel/mm/transparent_hugepage/hpage_pmd_size
All THPs at fault and collapse time will be added to _deferred_list,
and will therefore be split under memory presure if they are considered
"underused". A THP is underused if the number of zero-filled pages in
the THP is above max_ptes_none (see below). It is possible to disable
this behaviour by writing 0 to shrink_underused, and enable it by writing
1 to it::
echo 0 > /sys/kernel/mm/transparent_hugepage/shrink_underused
echo 1 > /sys/kernel/mm/transparent_hugepage/shrink_underused
khugepaged will be automatically started when PMD-sized THP is enabled
(either of the per-size anon control or the top-level control are set
to "always" or "madvise"), and it'll be automatically shutdown when
PMD-sized THP is disabled (when both the per-size anon control and the
top-level control are "never")
process THP controls
--------------------
A process can control its own THP behaviour using the ``PR_SET_THP_DISABLE``
and ``PR_GET_THP_DISABLE`` pair of prctl(2) calls. The THP behaviour set using
``PR_SET_THP_DISABLE`` is inherited across fork(2) and execve(2). These calls
support the following arguments::
prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0):
This will disable THPs completely for the process, irrespective
of global THP controls or madvise(..., MADV_COLLAPSE) being used.
prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0):
This will disable THPs for the process except when the usage of THPs is
advised. Consequently, THPs will only be used when:
- Global THP controls are set to "always" or "madvise" and
madvise(..., MADV_HUGEPAGE) or madvise(..., MADV_COLLAPSE) is used.
- Global THP controls are set to "never" and madvise(..., MADV_COLLAPSE)
is used. This is the same behavior as if THPs would not be disabled on
a process level.
Note that MADV_COLLAPSE is currently always rejected if
madvise(..., MADV_NOHUGEPAGE) is set on an area.
prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0):
This will re-enable THPs for the process, as if they were never disabled.
Whether THPs will actually be used depends on global THP controls and
madvise() calls.
prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0):
This returns a value whose bits indicate how THP-disable is configured:
Bits
1 0 Value Description
|0|0| 0 No THP-disable behaviour specified.
|0|1| 1 THP is entirely disabled for this process.
|1|1| 3 THP-except-advised mode is set for this process.
Khugepaged controls
-------------------
.. note::
khugepaged currently only searches for opportunities to collapse to
PMD-sized THP and no attempt is made to collapse to other THP
sizes.
khugepaged runs usually at low frequency so while one may not want to
invoke defrag algorithms synchronously during the page faults, it
should be worth invoking defrag at least in khugepaged. However it's
also possible to disable defrag in khugepaged by writing 0 or enable
defrag in khugepaged by writing 1::
echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
You can also control how many pages khugepaged should scan at each
pass::
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
and how many milliseconds to wait in khugepaged between each pass (you
can set this to 0 to run khugepaged at 100% utilization of one core)::
/sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs
and how many milliseconds to wait in khugepaged if there's an hugepage
allocation failure to throttle the next allocation attempt::
/sys/kernel/mm/transparent_hugepage/khugepaged/alloc_sleep_millisecs
The khugepaged progress can be seen in the number of pages collapsed (note
that this counter may not be an exact count of the number of pages
collapsed, since "collapsed" could mean multiple things: (1) A PTE mapping
being replaced by a PMD mapping, or (2) All 4K physical pages replaced by
one 2M hugepage. Each may happen independently, or together, depending on
the type of memory and the failures that occur. As such, this value should
be interpreted roughly as a sign of progress, and counters in /proc/vmstat
consulted for more accurate accounting)::
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed
for each pass::
/sys/kernel/mm/transparent_hugepage/khugepaged/full_scans
``max_ptes_none`` specifies how many extra small pages (that are
not already mapped) can be allocated when collapsing a group
of small pages into one large page::
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none
A higher value leads to use additional memory for programs.
A lower value leads to gain less thp performance. Value of
max_ptes_none can waste cpu time very little, you can
ignore it.
``max_ptes_swap`` specifies how many pages can be brought in from
swap when collapsing a group of pages into a transparent huge page::
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_swap
A higher value can cause excessive swap IO and waste
memory. A lower value can prevent THPs from being
collapsed, resulting fewer pages being collapsed into
THPs, and lower memory access performance.
``max_ptes_shared`` specifies how many pages can be shared across multiple
processes. khugepaged might treat pages of THPs as shared if any page of
that THP is shared. Exceeding the number would block the collapse::
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_shared
A higher value may increase memory footprint for some workloads.
Boot parameters
===============
You can change the sysfs boot time default for the top-level "enabled"
control by passing the parameter ``transparent_hugepage=always`` or
``transparent_hugepage=madvise`` or ``transparent_hugepage=never`` to the
kernel command line.
Alternatively, each supported anonymous THP size can be controlled by
passing ``thp_anon=<size>[KMG],<size>[KMG]:<state>;<size>[KMG]-<size>[KMG]:<state>``,
where ``<size>`` is the THP size (must be a power of 2 of PAGE_SIZE and
supported anonymous THP) and ``<state>`` is one of ``always``, ``madvise``,
``never`` or ``inherit``.
For example, the following will set 16K, 32K, 64K THP to ``always``,
set 128K, 512K to ``inherit``, set 256K to ``madvise`` and 1M, 2M
to ``never``::
thp_anon=16K-64K:always;128K,512K:inherit;256K:madvise;1M-2M:never
``thp_anon=`` may be specified multiple times to configure all THP sizes as
required. If ``thp_anon=`` is specified at least once, any anon THP sizes
not explicitly configured on the command line are implicitly set to
``never``.
``transparent_hugepage`` setting only affects the global toggle. If
``thp_anon`` is not specified, PMD_ORDER THP will default to ``inherit``.
However, if a valid ``thp_anon`` setting is provided by the user, the
PMD_ORDER THP policy will be overridden. If the policy for PMD_ORDER
is not defined within a valid ``thp_anon``, its policy will default to
``never``.
Similarly to ``transparent_hugepage``, you can control the hugepage
allocation policy for the internal shmem mount by using the kernel parameter
``transparent_hugepage_shmem=<policy>``, where ``<policy>`` is one of the
seven valid policies for shmem (``always``, ``within_size``, ``advise``,
``never``, ``deny``, and ``force``).
Similarly to ``transparent_hugepage_shmem``, you can control the default
hugepage allocation policy for the tmpfs mount by using the kernel parameter
``transparent_hugepage_tmpfs=<policy>``, where ``<policy>`` is one of the
four valid policies for tmpfs (``always``, ``within_size``, ``advise``,
``never``). The tmpfs mount default policy is ``never``.
In the same manner as ``thp_anon`` controls each supported anonymous THP
size, ``thp_shmem`` controls each supported shmem THP size. ``thp_shmem``
has the same format as ``thp_anon``, but also supports the policy
``within_size``.
``thp_shmem=`` may be specified multiple times to configure all THP sizes
as required. If ``thp_shmem=`` is specified at least once, any shmem THP
sizes not explicitly configured on the command line are implicitly set to
``never``.
``transparent_hugepage_shmem`` setting only affects the global toggle. If
``thp_shmem`` is not specified, PMD_ORDER hugepage will default to
``inherit``. However, if a valid ``thp_shmem`` setting is provided by the
user, the PMD_ORDER hugepage policy will be overridden. If the policy for
PMD_ORDER is not defined within a valid ``thp_shmem``, its policy will
default to ``never``.
Hugepages in tmpfs/shmem
========================
Traditionally, tmpfs only supported a single huge page size ("PMD"). Today,
it also supports smaller sizes just like anonymous memory, often referred
to as "multi-size THP" (mTHP). Huge pages of any size are commonly
represented in the kernel as "large folios".
While there is fine control over the huge page sizes to use for the internal
shmem mount (see below), ordinary tmpfs mounts will make use of all available
huge page sizes without any control over the exact sizes, behaving more like
other file systems.
tmpfs mounts
------------
The THP allocation policy for tmpfs mounts can be adjusted using the mount
option: ``huge=``. It can have following values:
always
Attempt to allocate huge pages every time we need a new page;
Always try PMD-sized huge pages first, and fall back to smaller-sized
huge pages if the PMD-sized huge page allocation fails;
never
Do not allocate huge pages. Note that ``madvise(..., MADV_COLLAPSE)``
can still cause transparent huge pages to be obtained even if this mode
is specified everywhere;
within_size
Only allocate huge page if it will be fully within i_size;
Always try PMD-sized huge pages first, and fall back to smaller-sized
huge pages if the PMD-sized huge page allocation fails;
Also respect madvise() hints;
advise
Only allocate huge pages if requested with madvise();
Remember, that the kernel may use huge pages of all available sizes, and
that no fine control as for the internal tmpfs mount is available.
The default policy in the past was ``never``, but it can now be adjusted
using the kernel parameter ``transparent_hugepage_tmpfs=<policy>``.
``mount -o remount,huge= /mountpoint`` works fine after mount: remounting
``huge=never`` will not attempt to break up huge pages at all, just stop more
from being allocated.
In addition to policies listed above, the sysfs knob
/sys/kernel/mm/transparent_hugepage/shmem_enabled will affect the
allocation policy of tmpfs mounts, when set to the following values:
deny
For use in emergencies, to force the huge option off from
all mounts;
force
Force the huge option on for all - very useful for testing;
shmem / internal tmpfs
----------------------
The mount internal tmpfs mount is used for SysV SHM, memfds, shared anonymous
mmaps (of /dev/zero or MAP_ANONYMOUS), GPU drivers' DRM objects, Ashmem.
To control the THP allocation policy for this internal tmpfs mount, the
sysfs knob /sys/kernel/mm/transparent_hugepage/shmem_enabled and the knobs
per THP size in
'/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/shmem_enabled'
can be used.
The global knob has the same semantics as the ``huge=`` mount options
for tmpfs mounts, except that the different huge page sizes can be controlled
individually, and will only use the setting of the global knob when the
per-size knob is set to 'inherit'.
The options 'force' and 'deny' are dropped for the individual sizes, which
are rather testing artifacts from the old ages.
always
Attempt to allocate <size> huge pages every time we need a new page;
inherit
Inherit the top-level "shmem_enabled" value. By default, PMD-sized hugepages
have enabled="inherit" and all other hugepage sizes have enabled="never";
never
Do not allocate <size> huge pages. Note that ``madvise(...,
MADV_COLLAPSE)`` can still cause transparent huge pages to be obtained
even if this mode is specified everywhere;
within_size
Only allocate <size> huge page if it will be fully within i_size.
Also respect madvise() hints;
advise
Only allocate <size> huge pages if requested with madvise();
Need of application restart
===========================
The transparent_hugepage/enabled and
transparent_hugepage/hugepages-<size>kB/enabled values and tmpfs mount
option only affect future behavior. So to make them effective you need
to restart any application that could have been using hugepages. This
also applies to the regions registered in khugepaged.
Monitoring usage
================
The number of PMD-sized anonymous transparent huge pages currently used by the
system is available by reading the AnonHugePages field in ``/proc/meminfo``.
To identify what applications are using PMD-sized anonymous transparent huge
pages, it is necessary to read ``/proc/PID/smaps`` and count the AnonHugePages
fields for each mapping. (Note that AnonHugePages only applies to traditional
PMD-sized THP for historical reasons and should have been called
AnonHugePmdMapped).
The number of file transparent huge pages mapped to userspace is available
by reading ShmemPmdMapped and ShmemHugePages fields in ``/proc/meminfo``.
To identify what applications are mapping file transparent huge pages, it
is necessary to read ``/proc/PID/smaps`` and count the FilePmdMapped fields
for each mapping.
Note that reading the smaps file is expensive and reading it
frequently will incur overhead.
There are a number of counters in ``/proc/vmstat`` that may be used to
monitor how successfully the system is providing huge pages for use.
thp_fault_alloc
is incremented every time a huge page is successfully
allocated and charged to handle a page fault.
thp_collapse_alloc
is incremented by khugepaged when it has found
a range of pages to collapse into one huge page and has
successfully allocated a new huge page to store the data.
thp_fault_fallback
is incremented if a page fault fails to allocate or charge
a huge page and instead falls back to using small pages.
thp_fault_fallback_charge
is incremented if a page fault fails to charge a huge page and
instead falls back to using small pages even though the
allocation was successful.
thp_collapse_alloc_failed
is incremented if khugepaged found a range
of pages that should be collapsed into one huge page but failed
the allocation.
thp_file_alloc
is incremented every time a shmem huge page is successfully
allocated (Note that despite being named after "file", the counter
measures only shmem).
thp_file_fallback
is incremented if a shmem huge page is attempted to be allocated
but fails and instead falls back to using small pages. (Note that
despite being named after "file", the counter measures only shmem).
thp_file_fallback_charge
is incremented if a shmem huge page cannot be charged and instead
falls back to using small pages even though the allocation was
successful. (Note that despite being named after "file", the
counter measures only shmem).
thp_file_mapped
is incremented every time a file or shmem huge page is mapped into
user address space.
thp_split_page
is incremented every time a huge page is split into base
pages. This can happen for a variety of reasons but a common
reason is that a huge page is old and is being reclaimed.
This action implies splitting all PMD the page mapped with.
thp_split_page_failed
is incremented if kernel fails to split huge
page. This can happen if the page was pinned by somebody.
thp_deferred_split_page
is incremented when a huge page is put onto split
queue. This happens when a huge page is partially unmapped and
splitting it would free up some memory. Pages on split queue are
going to be split under memory pressure.
thp_underused_split_page
is incremented when a huge page on the split queue was split
because it was underused. A THP is underused if the number of
zero pages in the THP is above a certain threshold
(/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none).
thp_split_pmd
is incremented every time a PMD split into table of PTEs.
This can happen, for instance, when application calls mprotect() or
munmap() on part of huge page. It doesn't split huge page, only
page table entry.
thp_zero_page_alloc
is incremented every time a huge zero page used for thp is
successfully allocated. Note, it doesn't count every map of
the huge zero page, only its allocation.
thp_zero_page_alloc_failed
is incremented if kernel fails to allocate
huge zero page and falls back to using small pages.
thp_swpout
is incremented every time a huge page is swapout in one
piece without splitting.
thp_swpout_fallback
is incremented if a huge page has to be split before swapout.
Usually because failed to allocate some continuous swap space
for the huge page.
In /sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/stats, There are
also individual counters for each huge page size, which can be utilized to
monitor the system's effectiveness in providing huge pages for usage. Each
counter has its own corresponding file.
anon_fault_alloc
is incremented every time a huge page is successfully
allocated and charged to handle a page fault.
anon_fault_fallback
is incremented if a page fault fails to allocate or charge
a huge page and instead falls back to using huge pages with
lower orders or small pages.
anon_fault_fallback_charge
is incremented if a page fault fails to charge a huge page and
instead falls back to using huge pages with lower orders or
small pages even though the allocation was successful.
zswpout
is incremented every time a huge page is swapped out to zswap in one
piece without splitting.
swpin
is incremented every time a huge page is swapped in from a non-zswap
swap device in one piece.
swpin_fallback
is incremented if swapin fails to allocate or charge a huge page
and instead falls back to using huge pages with lower orders or
small pages.
swpin_fallback_charge
is incremented if swapin fails to charge a huge page and instead
falls back to using huge pages with lower orders or small pages
even though the allocation was successful.
swpout
is incremented every time a huge page is swapped out to a non-zswap
swap device in one piece without splitting.
swpout_fallback
is incremented if a huge page has to be split before swapout.
Usually because failed to allocate some continuous swap space
for the huge page.
shmem_alloc
is incremented every time a shmem huge page is successfully
allocated.
shmem_fallback
is incremented if a shmem huge page is attempted to be allocated
but fails and instead falls back to using small pages.
shmem_fallback_charge
is incremented if a shmem huge page cannot be charged and instead
falls back to using small pages even though the allocation was
successful.
split
is incremented every time a huge page is successfully split into
smaller orders. This can happen for a variety of reasons but a
common reason is that a huge page is old and is being reclaimed.
split_failed
is incremented if kernel fails to split huge
page. This can happen if the page was pinned by somebody.
split_deferred
is incremented when a huge page is put onto split queue.
This happens when a huge page is partially unmapped and splitting
it would free up some memory. Pages on split queue are going to
be split under memory pressure, if splitting is possible.
nr_anon
the number of anonymous THP we have in the whole system. These THPs
might be currently entirely mapped or have partially unmapped/unused
subpages.
nr_anon_partially_mapped
the number of anonymous THP which are likely partially mapped, possibly
wasting memory, and have been queued for deferred memory reclamation.
Note that in corner some cases (e.g., failed migration), we might detect
an anonymous THP as "partially mapped" and count it here, even though it
is not actually partially mapped anymore.
As the system ages, allocating huge pages may be expensive as the
system uses memory compaction to copy data around memory to free a
huge page for use. There are some counters in ``/proc/vmstat`` to help
monitor this overhead.
compact_stall
is incremented every time a process stalls to run
memory compaction so that a huge page is free for use.
compact_success
is incremented if the system compacted memory and
freed a huge page for use.
compact_fail
is incremented if the system tries to compact memory
but failed.
It is possible to establish how long the stalls were using the function
tracer to record how long was spent in __alloc_pages() and
using the mm_page_alloc tracepoint to identify which allocations were
for huge pages.
Optimizing the applications
===========================
To be guaranteed that the kernel will map a THP immediately in any
memory region, the mmap region has to be hugepage naturally
aligned. posix_memalign() can provide that guarantee.
Hugetlbfs
=========
You can use hugetlbfs on a kernel that has transparent hugepage
support enabled just fine as always. No difference can be noted in
hugetlbfs other than there will be less overall fragmentation. All
usual features belonging to hugetlbfs are preserved and
unaffected. libhugetlbfs will also work fine as usual.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
목표와 전제
1-21큰 memory working set을 다루는 성능 중심 application은 이미 libhugetlbfs와 hugetlbfs를 사용합니다. Transparent HugePage Support(THP)는 virtual memory backing에 hugepage를 사용하는 대안으로, page size를 자동 승격·강등하며 hugetlbfs의 단점을 피합니다.
현재 THP는 anonymous memory mapping과 tmpfs/shmem에서 동작하며, 앞으로 다른 filesystem으로 확장될 수 있습니다.
이 문서의 예시는 기본 page size가 4K이고 hugepage size가 2M이라고 가정합니다. 실제 값은 CPU architecture에 따라 달라질 수 있습니다.
성능 이점
22-49첫 번째 이점은 user space가 건드린 각 2M virtual region에서 page fault를 한 번만 처리해 kernel 진입·복귀 빈도를 512배 줄이는 것입니다. 다만 mapping lifetime의 최초 접근에만 영향을 주고, fault에서 더 큰 clear-page·copy-page가 필요하다는 단점도 있어 중요도는 낮습니다.
두 번째이자 지속적인 핵심 이점은 모든 후속 memory access에 영향을 줍니다. TLB miss 처리 자체가 빨라지고, 하나의 TLB entry가 더 큰 virtual memory를 mapping해 miss 횟수가 줄어듭니다. Nested page table을 쓰는 virtualization에서는 KVM과 Linux guest 모두 hugepage를 쓰면 가장 큰 이득을 얻지만 한쪽만 사용해도 miss 처리 단축 효과가 있습니다.
| 요소 | 동작 | 효과 |
|---|---|---|
| Page fault | 2M virtual region당 최초 1회 | 4K page 대비 kernel 진입·복귀 빈도를 최대 512배 감소 |
| TLB | miss 처리 단축과 entry coverage 확대 | 전체 runtime 동안 miss 횟수와 비용을 줄임 |
Multi-size THP와 자동 collapse
50-76Modern kernel의 multi-size THP(mTHP)는 base page보다 크고 전통적인 PMD size보다 작은 power-of-2 page block을 지원합니다. Anonymous memory를 16K, 32K, 64K 등으로 backing할 수 있으며 PTE mapping을 유지합니다.
mTHP는 fault 횟수를 4배·8배·16배 등으로 줄이면서 PMD THP보다 page가 작아 clear해야 할 memory와 latency spike가 적습니다. 일부 architecture는 virtual·physical contiguous PTE 묶음에 TLB compression을 적용해 miss 빈도도 줄입니다.
THP는 system-wide, 특정 task, 또는 task address space의 특정 range로 제한할 수 있습니다. 완전히 비활성화하지 않으면 `khugepaged`가 memory를 scan해 basic page sequence를 PMD-size hugepage로 collapse합니다. 동작은 sysfs, madvise(2), prctl(2)로 제어합니다.
mTHP는 4K base page와 2M PMD THP 사이에서 fault 감소와 latency·memory 비용을 절충합니다.
hugetlbfs 대비 장점과 적용 범위
77-103THP는 hugetlbfs의 reservation 방식보다 free memory 활용도를 높입니다. 사용하지 않는 memory를 cache나 movable·unmovable entity에 쓸 수 있고, allocation 실패를 숨기기 위한 사전 예약이 필요하지 않으며 paging과 고급 VM 기능을 hugepage에서도 사용할 수 있습니다. Application 수정 없이도 이점을 얻습니다.
User space가 모든 malloc(4k)마다 mmap을 남발하지 않도록 최적화할 수 있지만 필수는 아닙니다. `khugepaged`는 hugepage를 모르는 application의 장기 page allocation도 처리합니다.
System-wide hugepage는 큰 region에서 1 byte만 건드려도 4K 대신 2M을 할당하는 낭비를 만들 수 있습니다. 그래서 system-wide로 끄고 `MADV_HUGEPAGE` region에서만 허용할 수 있습니다. Embedded system은 precious memory 낭비를 피하도록 madvise region으로 제한하는 편이 안전합니다.
Hugepage 이득이 크고 memory 손실 위험이 없는 application은 중요한 mmap region에 `madvise(MADV_HUGEPAGE)`를 사용해야 합니다.
THP size별·top-level enabled
104-150Anonymous memory THP는 지원되는 size별로 system-wide always, `MADV_HUGEPAGE` region 전용 madvise, 또는 never로 설정할 수 있습니다.
echo always >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
echo never >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
예를 들어 2M THP를 always로 설정합니다.
echo always >/sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled
해당 size가 top-level enabled 값을 상속하도록 지정할 수도 있습니다.
echo inherit >/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/enabled
2M THP 상속 예시는 다음과 같습니다.
echo inherit >/sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled
Top-level enabled 값은 다음 command 중 하나로 설정합니다.
echo always >/sys/kernel/mm/transparent_hugepage/enabled
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
echo never >/sys/kernel/mm/transparent_hugepage/enabled
| 상태 | 의미 |
|---|---|
| always | 지원되는 해당 THP size를 system-wide로 사용 |
| madvise | `MADV_HUGEPAGE`로 지정한 region에서 사용 |
| never | 일반 fault/collapse 경로에서 해당 size를 사용하지 않음 |
| inherit | top-level `transparent_hugepage/enabled` 값을 상속 |
모든 sysfs THP control을 never로 설정해도 global THP가 완전히 꺼지는 것은 아닙니다. `madvise(..., MADV_COLLAPSE)`는 이 설정을 무시하고 PMD-size hugepage로 무조건 collapse합니다.
기본값은 PMD-size hugepage의 enabled가 inherit이고 다른 size는 never입니다. 여러 size를 활성화하면 kernel이 allocation마다 가장 적절한 enabled size를 선택합니다.
Per-size inherit는 top-level 값을 사용하지만 process와 madvise hint가 실제 적용 범위를 더 좁히거나 `MADV_COLLAPSE`로 명시적 collapse를 요청할 수 있습니다.
Defrag 정책
151-199Anonymous hugepage가 즉시 free하지 않을 때 VM의 defrag 노력을 제한하거나, defrag 없이 regular page로 fallback하도록 선택할 수 있습니다. Defrag CPU 비용은 이후 hugepage 이득으로 상쇄되기를 기대하지만 보장되지는 않으며 `MADV_HUGEPAGE` allocation에서 가능성이 더 높습니다.
echo always >/sys/kernel/mm/transparent_hugepage/defrag
echo defer >/sys/kernel/mm/transparent_hugepage/defrag
echo defer+madvise >/sys/kernel/mm/transparent_hugepage/defrag
echo madvise >/sys/kernel/mm/transparent_hugepage/defrag
echo never >/sys/kernel/mm/transparent_hugepage/defrag
| mode | 동작 |
|---|---|
| always | allocation 실패 시 application을 멈추고 direct reclaim·compaction 수행 |
| defer | kswapd·kcompactd를 깨우고 khugepaged가 나중에 THP를 설치 |
| defer+madvise | `MADV_HUGEPAGE` region은 direct reclaim·compaction, 나머지는 background 처리 |
| madvise | `MADV_HUGEPAGE` region에서만 direct reclaim 수행하는 기본 mode |
| never | defrag를 시도하지 않고 regular page로 fallback; `MADV_COLLAPSE`는 예외 |
always는 즉시 THP를 얻기 위해 application stall을 허용하므로 THP 이득이 크고 시작 지연을 감수하는 VM에 적합합니다. defer는 background daemon을 깨우고 khugepaged가 나중에 설치하게 합니다. madvise 계열은 명시된 region에 direct reclaim·compaction 비용을 집중합니다.
Zero page와 underused THP
200-227Kernel은 anonymous read fault에서 기본적으로 PMD-mappable huge zero page를 사용합니다. 0을 기록하면 끄고 1을 기록하면 다시 켭니다.
echo 0 >/sys/kernel/mm/transparent_hugepage/use_zero_page
echo 1 >/sys/kernel/mm/transparent_hugepage/use_zero_page
User space는 다음 file에서 PMD-mappable THP size를 byte 단위로 읽을 수 있습니다.
cat /sys/kernel/mm/transparent_hugepage/hpage_pmd_size
Fault와 collapse 시 모든 THP는 `_deferred_list`에 들어가며 underused로 판단되면 memory pressure에서 split됩니다. THP의 zero-filled page 수가 `max_ptes_none`보다 많으면 underused입니다. `shrink_underused`에 0 또는 1을 기록해 이 동작을 끄거나 켭니다.
echo 0 > /sys/kernel/mm/transparent_hugepage/shrink_underused
echo 1 > /sys/kernel/mm/transparent_hugepage/shrink_underused
PMD-size THP의 per-size anon control 또는 top-level control이 always나 madvise이면 khugepaged가 자동 시작되고, 둘 다 never이면 자동 종료됩니다.
Process THP control
228-263Process는 prctl(2)의 `PR_SET_THP_DISABLE`과 `PR_GET_THP_DISABLE`로 자체 THP 동작을 제어합니다. `PR_SET_THP_DISABLE` 설정은 fork(2)와 execve(2)에 걸쳐 상속됩니다.
prctl(PR_SET_THP_DISABLE, 1, 0, 0, 0):
This will disable THPs completely for the process, irrespective
of global THP controls or madvise(..., MADV_COLLAPSE) being used.
prctl(PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0):
This will disable THPs for the process except when the usage of THPs is
advised. Consequently, THPs will only be used when:
- Global THP controls are set to "always" or "madvise" and
madvise(..., MADV_HUGEPAGE) or madvise(..., MADV_COLLAPSE) is used.
- Global THP controls are set to "never" and madvise(..., MADV_COLLAPSE)
is used. This is the same behavior as if THPs would not be disabled on
a process level.
Note that MADV_COLLAPSE is currently always rejected if
madvise(..., MADV_NOHUGEPAGE) is set on an area.
prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0):
This will re-enable THPs for the process, as if they were never disabled.
Whether THPs will actually be used depends on global THP controls and
madvise() calls.
prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0):
This returns a value whose bits indicate how THP-disable is configured:
Bits
1 0 Value Description
|0|0| 0 No THP-disable behaviour specified.
|0|1| 1 THP is entirely disabled for this process.
|1|1| 3 THP-except-advised mode is set for this process.
| 호출 | 효과 |
|---|---|
| `PR_SET_THP_DISABLE, 1, 0, 0, 0` | global control과 `MADV_COLLAPSE`에 관계없이 process THP 완전 비활성화 |
| `PR_SET_THP_DISABLE, 1, PR_THP_DISABLE_EXCEPT_ADVISED, 0, 0` | 명시적으로 권고된 region에서만 THP 허용 |
| `PR_SET_THP_DISABLE, 0, 0, 0, 0` | process-level disable을 해제하고 global·madvise 정책으로 복귀 |
| `PR_GET_THP_DISABLE, 0, 0, 0, 0` | THP-disable 설정 bit를 반환 |
| bits | 값 | 설명 |
|---|---|---|
| `|0|0|` | 0 | THP-disable 동작을 지정하지 않음 |
| `|0|1|` | 1 | 이 process에서 THP 완전 비활성화 |
| `|1|1|` | 3 | THP-except-advised mode |
`PR_THP_DISABLE_EXCEPT_ADVISED` mode에서도 `MADV_NOHUGEPAGE`가 설정된 area에는 `MADV_COLLAPSE`가 항상 거부됩니다.
Khugepaged scan과 진행량
264-310현재 khugepaged는 PMD-size THP로 collapse할 기회만 찾으며 다른 THP size로 collapse하지 않습니다.
khugepaged는 보통 낮은 빈도로 실행되므로 page fault에서 synchronous defrag를 피하더라도 background에서는 defrag를 수행할 가치가 있습니다. 다음 setting으로 khugepaged defrag를 끄거나 켭니다.
echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
한 pass에서 scan할 page 수를 제어합니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
Pass 사이 대기 millisecond를 지정하며 0이면 한 core를 100% 사용합니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs
Hugepage allocation 실패 뒤 다음 allocation 시도까지의 대기 시간을 지정합니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/alloc_sleep_millisecs
`pages_collapsed`는 PTE mapping을 PMD로 바꾸거나 여러 4K physical page를 하나의 2M hugepage로 교체하는 서로 다른 의미를 포함할 수 있어 대략적인 진행 신호로 해석해야 합니다. 정확한 accounting에는 `/proc/vmstat`을 사용합니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed
완료한 full scan 수는 다음 file에서 봅니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/full_scans
| 항목 | sysfs | 의미 |
|---|---|---|
| defrag | `khugepaged/defrag` | background collapse를 위한 memory compaction 허용 |
| scan 양 | `pages_to_scan` | 한 pass에서 검사할 page 수 |
| pass 간격 | `scan_sleep_millisecs` | pass 사이 대기 시간; 0이면 한 core를 100% 사용 |
| 실패 간격 | `alloc_sleep_millisecs` | hugepage allocation 실패 뒤 다음 시도까지 대기 |
| 진행량 | `pages_collapsed` | collapse 진행 신호이며 정확한 accounting은 `/proc/vmstat` 사용 |
| pass 수 | `full_scans` | 완료한 full scan 횟수 |
Khugepaged collapse threshold
311-339`max_ptes_none`은 small page group을 하나의 large page로 collapse할 때 추가 할당할 수 있는 아직 mapping되지 않은 small page 수입니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_none
값이 높으면 program memory 사용이 늘고, 낮으면 THP 성능 이득이 줄 수 있습니다. CPU 낭비는 매우 작습니다.
`max_ptes_swap`은 collapse 중 swap에서 가져올 수 있는 page 수입니다. 높으면 과도한 swap I/O와 memory 낭비를 만들고, 낮으면 collapse가 막혀 memory access 성능이 낮아질 수 있습니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_swap
`max_ptes_shared`는 여러 process가 공유해도 collapse를 허용할 page 수입니다. THP의 page 하나라도 공유되면 khugepaged가 THP page를 shared로 취급할 수 있으며 threshold 초과 시 collapse를 막습니다. 높은 값은 일부 workload의 footprint를 늘립니다.
/sys/kernel/mm/transparent_hugepage/khugepaged/max_ptes_shared
| 항목 | sysfs | tradeoff |
|---|---|---|
| 빈 PTE | `max_ptes_none` | collapse 중 새로 할당할 수 있는 아직 mapping되지 않은 small page 수 |
| swap PTE | `max_ptes_swap` | collapse 중 swap에서 가져올 수 있는 page 수 |
| 공유 PTE | `max_ptes_shared` | 여러 process가 공유해도 collapse를 허용할 page 수 |
Boot parameter
340-400Kernel command line의 `transparent_hugepage=always`, `transparent_hugepage=madvise`, `transparent_hugepage=never`로 top-level enabled의 boot 기본값을 바꿀 수 있습니다.
지원되는 anonymous THP size별 정책은 `thp_anon=<size>[KMG],<size>[KMG]:<state>;<size>[KMG]-<size>[KMG]:<state>` 형식으로 설정합니다. Size는 PAGE_SIZE의 power of 2이면서 지원되는 anonymous THP여야 하며 state는 always, madvise, never, inherit 중 하나입니다.
다음 예시는 16K–64K를 always, 128K와 512K를 inherit, 256K를 madvise, 1M–2M을 never로 설정합니다.
thp_anon=16K-64K:always;128K,512K:inherit;256K:madvise;1M-2M:never
`thp_anon=`은 여러 번 지정할 수 있습니다. 한 번이라도 지정하면 command line에서 명시하지 않은 anonymous THP size는 implicitly never가 됩니다. 유효한 설정이 있으면 PMD_ORDER policy도 override되며 명시하지 않은 PMD_ORDER는 never가 됩니다.
`transparent_hugepage_shmem=<policy>`는 internal shmem mount의 global allocation 정책을, `transparent_hugepage_tmpfs=<policy>`는 일반 tmpfs mount의 기본 정책을 정합니다. tmpfs 기본값은 never입니다.
`thp_shmem`은 `thp_anon`과 같은 형식으로 shmem size별 정책을 제어하고 within_size도 지원합니다. 한 번이라도 지정하면 명시하지 않은 size는 never이며, 유효한 설정은 PMD_ORDER의 inherit 기본값을 override합니다.
| parameter | 값 | 범위 |
|---|---|---|
| `transparent_hugepage=` | always / madvise / never | top-level anonymous THP toggle의 boot 기본값 |
| `thp_anon=` | size 또는 range별 always / madvise / never / inherit | anonymous mTHP size별 정책 |
| `transparent_hugepage_shmem=` | shmem global policy | internal shmem mount의 allocation 정책 |
| `transparent_hugepage_tmpfs=` | always / within_size / advise / never | 일반 tmpfs mount의 기본 정책 |
| `thp_shmem=` | `thp_anon` 형식 + within_size | shmem THP size별 정책 |
tmpfs/shmem의 hugepage
401-413과거 tmpfs는 PMD라는 단일 hugepage size만 지원했지만 이제 anonymous memory처럼 smaller mTHP를 지원합니다. Kernel에서는 모든 size의 hugepage를 흔히 large folio로 표현합니다.
Internal shmem mount는 size를 세밀하게 제어할 수 있지만 일반 tmpfs mount는 정확한 size를 지정하지 않고 사용 가능한 모든 hugepage size를 활용해 다른 filesystem과 비슷하게 동작합니다.
일반 tmpfs mount 정책
414-458tmpfs mount의 THP allocation policy는 `huge=` mount option으로 조정합니다.
| policy | 동작 |
|---|---|
| always | 새 page마다 hugepage를 시도하고 PMD size 실패 시 smaller size로 fallback |
| never | hugepage를 할당하지 않음; `MADV_COLLAPSE`는 예외 |
| within_size | `i_size` 안에 완전히 들어갈 때만 할당하며 madvise hint도 존중 |
| advise | madvise로 요청한 경우에만 할당 |
| deny | 긴급 시 모든 tmpfs mount의 huge option을 강제로 끔 |
| force | 시험을 위해 모든 mount의 huge option을 강제로 켬 |
Kernel은 사용 가능한 모든 size를 쓸 수 있으며 internal tmpfs처럼 세밀한 size control은 제공하지 않습니다. 과거 기본값은 never였지만 이제 `transparent_hugepage_tmpfs=<policy>` boot parameter로 조정할 수 있습니다.
Mount 뒤에도 `mount -o remount,huge= /mountpoint`가 동작합니다. huge=never로 remount하면 기존 hugepage를 분할하지 않고 새 allocation만 중단합니다.
Global `/sys/kernel/mm/transparent_hugepage/shmem_enabled`의 deny는 긴급 시 모든 mount의 huge option을 끄고, force는 시험을 위해 모두 켭니다.
Internal shmem mount
459-496Internal tmpfs mount는 SysV SHM, memfd, `/dev/zero` 또는 `MAP_ANONYMOUS`의 shared anonymous mmap, GPU driver의 DRM object, Ashmem에 사용됩니다.
Global `/sys/kernel/mm/transparent_hugepage/shmem_enabled`와 size별 `/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/shmem_enabled`로 THP allocation policy를 제어합니다.
Global knob은 tmpfs의 huge= option과 같은 의미지만 size를 개별 제어할 수 있고 per-size knob이 inherit일 때만 global 값을 사용합니다. 과거 시험용 artifact인 force와 deny는 개별 size에서 제거되었습니다.
| policy | 해당 size 동작 |
|---|---|
| always | 새 page가 필요할 때 해당 `<size>` hugepage 할당 시도 |
| inherit | top-level `shmem_enabled` 값을 상속 |
| never | 해당 `<size>` hugepage를 할당하지 않음; `MADV_COLLAPSE`는 예외 |
| within_size | `i_size` 안에 완전히 들어갈 때만 할당하고 madvise hint 존중 |
| advise | madvise 요청이 있을 때만 해당 size 할당 |
Application restart 필요성
497-505`transparent_hugepage/enabled`, `transparent_hugepage/hugepages-<size>kB/enabled`, tmpfs mount option은 향후 동작에만 영향을 줍니다. Hugepage를 사용했을 수 있는 application을 restart해야 새 설정이 적용되며, khugepaged에 등록된 region도 마찬가지입니다.
사용량 관찰
506-532현재 사용 중인 PMD-size anonymous THP 수는 `/proc/meminfo`의 AnonHugePages에서 읽습니다. Application별 사용량은 `/proc/PID/smaps`의 mapping별 AnonHugePages를 합산합니다. 역사적 이유로 이 field는 PMD THP에만 적용되며 정확히는 AnonHugePmdMapped라는 이름이 더 적절합니다.
User space에 mapping된 file THP는 `/proc/meminfo`의 ShmemPmdMapped와 ShmemHugePages로 보고, application별 mapping은 `/proc/PID/smaps`의 FilePmdMapped를 합산합니다.
| interface | 의미 |
|---|---|
| `/proc/meminfo: AnonHugePages` | 현재 사용 중인 PMD-size anonymous THP |
| `/proc/PID/smaps: AnonHugePages` | mapping별 PMD-size anonymous THP |
| `/proc/meminfo: ShmemPmdMapped`, `ShmemHugePages` | user space에 mapping된 file/shmem THP |
| `/proc/PID/smaps: FilePmdMapped` | application mapping별 file THP |
smaps 판독은 비싸므로 자주 읽으면 overhead가 발생합니다. Hugepage 제공 성공률은 `/proc/vmstat` counter로 관찰할 수 있습니다.
전역 THP vmstat counter
533-612다음 `/proc/vmstat` counter는 fault allocation, khugepaged collapse, fallback, mapping, split, zero page와 swapout 결과를 구분합니다. 이름에 file이 들어간 일부 counter는 실제로 shmem만 측정한다는 점에 주의합니다.
| counter | 증가 조건 |
|---|---|
| thp_fault_alloc | page fault 처리용 hugepage allocation·charge 성공 |
| thp_collapse_alloc | khugepaged collapse용 새 hugepage allocation 성공 |
| thp_fault_fallback | fault에서 hugepage 확보 실패 후 small page 사용 |
| thp_fault_fallback_charge | allocation은 성공했지만 charge 실패 후 small page 사용 |
| thp_collapse_alloc_failed | collapse 대상은 찾았지만 hugepage allocation 실패 |
| thp_file_alloc | shmem hugepage allocation 성공 |
| thp_file_fallback | shmem hugepage allocation 실패 후 small page 사용 |
| thp_file_fallback_charge | shmem hugepage charge 실패 후 small page 사용 |
| thp_file_mapped | file 또는 shmem hugepage를 user address space에 mapping |
| thp_split_page | hugepage를 base page로 분할 |
| thp_split_page_failed | pin 등의 이유로 hugepage 분할 실패 |
| thp_deferred_split_page | partially unmapped hugepage를 split queue에 추가 |
| thp_underused_split_page | zero page 수가 threshold를 넘은 underused THP를 split |
| thp_split_pmd | PMD를 PTE table로 분할하며 physical hugepage는 유지 |
| thp_zero_page_alloc | huge zero page 자체 allocation 성공 |
| thp_zero_page_alloc_failed | huge zero page allocation 실패 후 small page 사용 |
| thp_swpout | hugepage를 분할하지 않고 한 덩어리로 swapout |
| thp_swpout_fallback | 연속 swap 공간 부족 등으로 swapout 전에 hugepage 분할 |
THP size별 stats
613-696`/sys/kernel/mm/transparent_hugepage/hugepages-<size>kB/stats`에는 각 hugepage size별 counter file이 있습니다. Anonymous fault, zswap·swap I/O, shmem, split과 현재 anonymous THP 수를 size별로 관찰할 수 있습니다.
| counter | 증가 또는 값의 의미 |
|---|---|
| anon_fault_alloc | 해당 size anonymous hugepage fault allocation·charge 성공 |
| anon_fault_fallback | allocation 또는 charge 실패 후 lower-order hugepage나 small page 사용 |
| anon_fault_fallback_charge | allocation 성공·charge 실패 후 lower-order hugepage나 small page 사용 |
| zswpout | hugepage를 분할하지 않고 zswap으로 swapout |
| swpin | non-zswap device에서 hugepage를 한 덩어리로 swapin |
| swpin_fallback | swapin allocation·charge 실패 후 lower-order hugepage나 small page 사용 |
| swpin_fallback_charge | swapin allocation 성공·charge 실패 후 lower-order 또는 small page 사용 |
| swpout | non-zswap device로 hugepage를 한 덩어리로 swapout |
| swpout_fallback | 연속 swap 공간 부족 등으로 swapout 전에 분할 |
| shmem_alloc | shmem hugepage allocation 성공 |
| shmem_fallback | shmem hugepage allocation 실패 후 small page 사용 |
| shmem_fallback_charge | shmem hugepage charge 실패 후 small page 사용 |
| split | hugepage를 smaller order로 분할 성공 |
| split_failed | pin 등의 이유로 분할 실패 |
| split_deferred | partially unmapped hugepage를 split queue에 추가 |
| nr_anon | system 전체 anonymous THP 수 |
| nr_anon_partially_mapped | 부분 mapping으로 판단되어 deferred reclaim queue에 들어간 anonymous THP 수 |
`nr_anon_partially_mapped`는 deferred reclaim 대상 수를 나타내지만 migration 실패 같은 corner case에서는 실제로 더 이상 partial mapping이 아닌 THP가 포함될 수 있습니다.
Compaction overhead 관찰
697-725System이 오래 실행될수록 hugepage 확보를 위해 memory compaction이 data를 이동해야 하므로 allocation 비용이 커질 수 있습니다. `/proc/vmstat`의 다음 counter로 이 overhead를 관찰합니다.
| counter | 의미 |
|---|---|
| compact_stall | hugepage 확보를 위해 process가 compaction에서 stall |
| compact_success | compaction으로 hugepage 공간 확보 성공 |
| compact_fail | compaction을 시도했지만 공간 확보 실패 |
Function tracer로 `__alloc_pages()`에 머문 시간을 기록하고 `mm_page_alloc` tracepoint로 hugepage allocation을 식별하면 stall 시간을 측정할 수 있습니다.
Application 최적화
726-732Kernel이 어떤 memory region에서든 즉시 THP를 mapping하도록 보장하려면 mmap region이 hugepage natural alignment를 만족해야 합니다. `posix_memalign()`이 이를 보장할 수 있습니다.
Hugetlbfs와의 공존
733-740Transparent hugepage support가 활성화된 kernel에서도 hugetlbfs를 평소처럼 사용할 수 있습니다. 전체 fragmentation이 줄어드는 것 외에는 차이가 없고 hugetlbfs의 기존 기능은 보존되며 영향을 받지 않습니다. libhugetlbfs도 그대로 동작합니다.
운영 핵심
transhuge.rst:1-740THP는 page size를 자동 승격·강등해 TLB miss와 page fault를 줄이지만 allocation latency와 memory 낭비가 생길 수 있습니다. Size별 enabled·defrag, process prctl, madvise, khugepaged와 shmem policy를 workload에 맞게 조합하고 vmstat·size별 stats로 결과를 검증해야 합니다.