Skip to content

Commit 8738f27

Browse files
TCMalloc Teamcopybara-github
TCMalloc Team
authored andcommitted
Subrelease stats: track number of subreleased pages and number of hugepages broken
We track this cumulatively from startup as well as for each epoch covered by the timeseries tracker PiperOrigin-RevId: 318574429 Change-Id: Ic396f113a412b5c4840a2648a118534895528dfb
1 parent 2bba66b commit 8738f27

File tree

4 files changed

+212
-11
lines changed

4 files changed

+212
-11
lines changed

docs/stats.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ HugePageFiller: among non-fulls, 0.0398 free
495495
HugePageFiller: 499 used pages in subreleased hugepages (0 of them in partially released)
496496
HugePageFiller: 0 hugepages partially released, 0.0000 released
497497
HugePageFiller: 1.0000 of used pages hugepageable
498+
HugePageFiller: Since startup, 26159 pages subreleased, 345 hugepages broken
498499
```
499500

500501
The summary stats are as follows:

tcmalloc/huge_page_aware_allocator.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ Length HugePageAwareAllocator::ReleaseAtLeastNPages(Length num_pages) {
465465
if (Parameters::hpaa_subrelease()) {
466466
if (released < num_pages) {
467467
released += filler_.ReleasePages(
468-
num_pages - released, Parameters::filler_skip_subrelease_interval());
468+
num_pages - released, Parameters::filler_skip_subrelease_interval(),
469+
/*hit_limit*/ false);
469470
}
470471
}
471472

@@ -621,7 +622,7 @@ void *HugePageAwareAllocator::MetaDataAlloc(size_t bytes)
621622
Length HugePageAwareAllocator::ReleaseAtLeastNPagesBreakingHugepages(Length n) {
622623
// We desparately need to release memory, and are willing to
623624
// compromise on hugepage usage. That means releasing from the filler.
624-
return filler_.ReleasePages(n, absl::ZeroDuration());
625+
return filler_.ReleasePages(n, absl::ZeroDuration(), /*hit_limit*/ true);
625626
}
626627

627628
void HugePageAwareAllocator::UnbackWithoutLock(void *start, size_t length) {

tcmalloc/huge_page_filler.h

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,32 @@ class SkippedSubreleaseCorrectnessTracker {
193193
tracker_;
194194
};
195195

196+
struct SubreleaseStats {
197+
Length total_pages_subreleased = 0; // cumulative since startup
198+
Length num_pages_subreleased = 0;
199+
HugeLength total_hugepages_broken{NHugePages(0)}; // cumulative since startup
200+
HugeLength num_hugepages_broken{NHugePages(0)};
201+
202+
bool is_limit_hit = false;
203+
// Keep these limit-related stats cumulative since startup only
204+
Length total_pages_subreleased_due_to_limit = 0;
205+
HugeLength total_hugepages_broken_due_to_limit{NHugePages(0)};
206+
207+
void reset() {
208+
total_pages_subreleased += num_pages_subreleased;
209+
total_hugepages_broken += num_hugepages_broken;
210+
num_pages_subreleased = 0;
211+
num_hugepages_broken = NHugePages(0);
212+
}
213+
214+
// Must be called at the beginning of each subrelease request
215+
void set_limit_hit(bool value) { is_limit_hit = value; }
216+
217+
// This only has a well-defined meaning within ReleaseCandidates where
218+
// set_limit_hit() has been called earlier. Do not use anywhere else.
219+
bool limit_hit() { return is_limit_hit; }
220+
};
221+
196222
// Track filler statistics over a time window.
197223
template <size_t kEpochs = 16>
198224
class FillerStatsTracker {
@@ -205,6 +231,8 @@ class FillerStatsTracker {
205231
Length unmapped_pages = 0;
206232
Length used_pages_in_subreleased_huge_pages = 0;
207233
HugeLength huge_pages[kNumTypes];
234+
Length num_pages_subreleased = 0;
235+
HugeLength num_hugepages_broken = NHugePages(0);
208236

209237
HugeLength total_huge_pages() const {
210238
HugeLength total_huge_pages;
@@ -351,6 +379,8 @@ class FillerStatsTracker {
351379
static constexpr Length kDefaultValue = std::numeric_limits<Length>::max();
352380
Length min_free_pages = kDefaultValue;
353381
Length min_free_backed_pages = kDefaultValue;
382+
Length num_pages_subreleased = 0;
383+
HugeLength num_hugepages_broken = NHugePages(0);
354384

355385
static FillerStatsEntry Nil() { return FillerStatsEntry(); }
356386

@@ -382,6 +412,10 @@ class FillerStatsTracker {
382412
min_free_pages =
383413
std::min(min_free_pages, e.free_pages + e.unmapped_pages);
384414
min_free_backed_pages = std::min(min_free_backed_pages, e.free_pages);
415+
416+
// Subrelease stats
417+
num_pages_subreleased += e.num_pages_subreleased;
418+
num_hugepages_broken += e.num_hugepages_broken;
385419
}
386420

387421
bool empty() const { return min_free_pages == kDefaultValue; }
@@ -481,9 +515,24 @@ void FillerStatsTracker<kEpochs>::Print(TCMalloc_Printer *out) const {
481515

482516
out->printf(
483517
"HugePageFiller: %.4f%% of decisions confirmed correct, %zu "
484-
"pending (%.4f%% of pages, %zu pending).",
518+
"pending (%.4f%% of pages, %zu pending).\n",
485519
correctly_skipped_count_percentage, pending_skipped().count,
486520
correctly_skipped_pages_percentage, pending_skipped().pages);
521+
522+
// Print subrelease stats
523+
Length total_subreleased = 0;
524+
HugeLength total_broken = NHugePages(0);
525+
tracker_.Iter(
526+
[&](size_t offset, int64_t ts, const FillerStatsEntry &e) {
527+
total_subreleased += e.num_pages_subreleased;
528+
total_broken += e.num_hugepages_broken;
529+
},
530+
tracker_.kSkipEmptyEntries);
531+
out->printf(
532+
"HugePageFiller: Subrelease stats last %d min: total "
533+
"%zu pages subreleased, %zu hugepages broken\n",
534+
static_cast<int64_t>(absl::ToInt64Minutes(window_)), total_subreleased,
535+
total_broken.raw_num());
487536
}
488537

489538
template <size_t kEpochs>
@@ -526,6 +575,9 @@ void FillerStatsTracker<kEpochs>::PrintInPbtxt(PbtxtRegion *hpaa) const {
526575
absl::ToInt64Milliseconds(absl::Nanoseconds(ts)));
527576
region.PrintI64("min_free_pages", e.min_free_pages);
528577
region.PrintI64("min_free_backed_pages", e.min_free_backed_pages);
578+
region.PrintI64("num_pages_subreleased", e.num_pages_subreleased);
579+
region.PrintI64("num_hugepages_broken",
580+
e.num_hugepages_broken.raw_num());
529581
for (int i = 0; i < kNumStatsTypes; i++) {
530582
auto m = region.CreateSubRegion(labels[i]);
531583
FillerStats stats = e.stats[i];
@@ -754,13 +806,15 @@ class HugePageFiller {
754806
// possible hugepage and releasing its free memory to the system. Return the
755807
// number of pages actually released.
756808
Length ReleasePages(Length desired,
757-
absl::Duration skip_subrelease_after_peaks_interval)
809+
absl::Duration skip_subrelease_after_peaks_interval,
810+
bool hit_limit)
758811
ABSL_EXCLUSIVE_LOCKS_REQUIRED(pageheap_lock);
759812

760813
void AddSpanStats(SmallSpanStats *small, LargeSpanStats *large,
761814
PageAgeHistograms *ages) const;
762815

763816
BackingStats stats() const;
817+
SubreleaseStats subrelease_stats() const { return subrelease_stats_; }
764818
void Print(TCMalloc_Printer *out, bool everything) const;
765819
void PrintInPbtxt(PbtxtRegion *hpaa) const;
766820

@@ -842,6 +896,8 @@ class HugePageFiller {
842896
HugeLength size_;
843897
};
844898

899+
SubreleaseStats subrelease_stats_;
900+
845901
// We group hugepages first by longest-free (as a measure of fragmentation),
846902
// then into 8 chunks inside there by desirability of allocation.
847903
static constexpr size_t kChunks = 8;
@@ -1335,6 +1391,7 @@ inline Length HugePageFiller<TrackerType>::ReleaseCandidates(
13351391
absl::c_sort(candidates, CompareForSubrelease);
13361392

13371393
Length total_released = 0;
1394+
HugeLength total_broken = NHugePages(0);
13381395
#ifndef NDEBUG
13391396
Length last = 0;
13401397
#endif
@@ -1348,6 +1405,12 @@ inline Length HugePageFiller<TrackerType>::ReleaseCandidates(
13481405
last = best->used_pages();
13491406
#endif
13501407

1408+
// We use !released() to figure out whether a hugepage is unbroken or not
1409+
// TODO(b/160020285): Use a boolean to track the transition of a hugepage
1410+
// from unbroken->broken and vice versa. Only the former is being captured.
1411+
if (!best->released()) {
1412+
++total_broken;
1413+
}
13511414
RemoveFromFillerList(best);
13521415
Length ret = best->ReleaseFree();
13531416
unmapped_ += ret;
@@ -1356,6 +1419,15 @@ inline Length HugePageFiller<TrackerType>::ReleaseCandidates(
13561419
AddToFillerList(best);
13571420
}
13581421

1422+
subrelease_stats_.num_pages_subreleased += total_released;
1423+
subrelease_stats_.num_hugepages_broken += total_broken;
1424+
1425+
// Keep separate stats if the on going release is triggered by reaching
1426+
// tcmalloc limit
1427+
if (subrelease_stats_.limit_hit()) {
1428+
subrelease_stats_.total_pages_subreleased_due_to_limit += total_released;
1429+
subrelease_stats_.total_hugepages_broken_due_to_limit += total_broken;
1430+
}
13591431
return total_released;
13601432
}
13611433

@@ -1407,7 +1479,8 @@ inline Length HugePageFiller<TrackerType>::GetDesiredSubreleasePages(
14071479
// number of pages actually released.
14081480
template <class TrackerType>
14091481
inline Length HugePageFiller<TrackerType>::ReleasePages(
1410-
Length desired, absl::Duration skip_subrelease_after_peaks_interval) {
1482+
Length desired, absl::Duration skip_subrelease_after_peaks_interval,
1483+
bool hit_limit) {
14111484
Length total_released = 0;
14121485

14131486
// We also do eager release, once we've called this at least once:
@@ -1417,6 +1490,7 @@ inline Length HugePageFiller<TrackerType>::ReleasePages(
14171490
// pages.
14181491
Length n = unmapping_unaccounted_;
14191492
unmapping_unaccounted_ = 0;
1493+
subrelease_stats_.num_pages_subreleased += n;
14201494

14211495
if (n >= desired) {
14221496
return n;
@@ -1433,6 +1507,8 @@ inline Length HugePageFiller<TrackerType>::ReleasePages(
14331507
}
14341508
}
14351509

1510+
subrelease_stats_.set_limit_hit(hit_limit);
1511+
14361512
// Optimize for releasing up to a huge page worth of small pages (scattered
14371513
// over many parts of the filler). Since we hold pageheap_lock, we cannot
14381514
// allocate here.
@@ -1693,6 +1769,16 @@ inline void HugePageFiller<TrackerType>::Print(TCMalloc_Printer *out,
16931769
nrel.raw_num(), safe_div(unmapped_pages(), nrel.in_pages()));
16941770
out->printf("HugePageFiller: %.4f of used pages hugepageable\n",
16951771
hugepage_frac());
1772+
1773+
// Subrelease
1774+
out->printf(
1775+
"HugePageFiller: Since startup, %zu pages subreleased, %zu hugepages "
1776+
"broken, (%zu pages, %zu hugepages due to reaching tcmalloc limit)\n",
1777+
subrelease_stats_.total_pages_subreleased,
1778+
subrelease_stats_.total_hugepages_broken.raw_num(),
1779+
subrelease_stats_.total_pages_subreleased_due_to_limit,
1780+
subrelease_stats_.total_hugepages_broken_due_to_limit.raw_num());
1781+
16961782
if (!everything) return;
16971783

16981784
// Compute some histograms of fullness.
@@ -1722,7 +1808,6 @@ inline void HugePageFiller<TrackerType>::Print(TCMalloc_Printer *out,
17221808

17231809
out->printf("\n");
17241810
fillerstats_tracker_.Print(out);
1725-
out->printf("\n");
17261811
}
17271812

17281813
template <class TrackerType>
@@ -1759,7 +1844,15 @@ inline void HugePageFiller<TrackerType>::PrintInPbtxt(PbtxtRegion *hpaa) const {
17591844
"filler_hugepageable_used_bytes",
17601845
static_cast<uint64_t>(hugepage_frac() *
17611846
static_cast<double>(allocated_ * kPageSize)));
1762-
1847+
hpaa->PrintI64("filler_num_pages_subreleased",
1848+
subrelease_stats_.total_pages_subreleased);
1849+
hpaa->PrintI64("filler_num_hugepages_broken",
1850+
subrelease_stats_.total_hugepages_broken.raw_num());
1851+
hpaa->PrintI64("filler_num_pages_subreleased_due_to_limit",
1852+
subrelease_stats_.total_pages_subreleased_due_to_limit);
1853+
hpaa->PrintI64(
1854+
"filler_num_hugepages_broken_due_to_limit",
1855+
subrelease_stats_.total_hugepages_broken_due_to_limit.raw_num());
17631856
// Compute some histograms of fullness.
17641857
using ::tcmalloc::internal::UsageInfo;
17651858
UsageInfo usage;
@@ -1796,7 +1889,10 @@ inline void HugePageFiller<TrackerType>::UpdateFillerStatsTracker() {
17961889
n_used_partial_released_ + n_used_released_,
17971890
.huge_pages = {regular_alloc_.size(), donated_alloc_.size(),
17981891
regular_alloc_partial_released_.size(),
1799-
regular_alloc_released_.size()}});
1892+
regular_alloc_released_.size()},
1893+
.num_pages_subreleased = subrelease_stats_.num_pages_subreleased,
1894+
.num_hugepages_broken = subrelease_stats_.num_hugepages_broken});
1895+
subrelease_stats_.reset();
18001896
}
18011897

18021898
template <class TrackerType>

0 commit comments

Comments
 (0)