@@ -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.
197223template <size_t kEpochs = 16 >
198224class 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
489538template <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.
14081480template <class TrackerType >
14091481inline 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
17281813template <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
18021898template <class TrackerType >
0 commit comments