@@ -193,6 +193,32 @@ class SkippedSubreleaseCorrectnessTracker {
193
193
tracker_;
194
194
};
195
195
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
+
196
222
// Track filler statistics over a time window.
197
223
template <size_t kEpochs = 16 >
198
224
class FillerStatsTracker {
@@ -205,6 +231,8 @@ class FillerStatsTracker {
205
231
Length unmapped_pages = 0 ;
206
232
Length used_pages_in_subreleased_huge_pages = 0 ;
207
233
HugeLength huge_pages[kNumTypes ];
234
+ Length num_pages_subreleased = 0 ;
235
+ HugeLength num_hugepages_broken = NHugePages(0 );
208
236
209
237
HugeLength total_huge_pages () const {
210
238
HugeLength total_huge_pages;
@@ -351,6 +379,8 @@ class FillerStatsTracker {
351
379
static constexpr Length kDefaultValue = std::numeric_limits<Length>::max();
352
380
Length min_free_pages = kDefaultValue ;
353
381
Length min_free_backed_pages = kDefaultValue ;
382
+ Length num_pages_subreleased = 0 ;
383
+ HugeLength num_hugepages_broken = NHugePages(0 );
354
384
355
385
static FillerStatsEntry Nil () { return FillerStatsEntry (); }
356
386
@@ -382,6 +412,10 @@ class FillerStatsTracker {
382
412
min_free_pages =
383
413
std::min (min_free_pages, e.free_pages + e.unmapped_pages );
384
414
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 ;
385
419
}
386
420
387
421
bool empty () const { return min_free_pages == kDefaultValue ; }
@@ -481,9 +515,24 @@ void FillerStatsTracker<kEpochs>::Print(TCMalloc_Printer *out) const {
481
515
482
516
out->printf (
483
517
" HugePageFiller: %.4f%% of decisions confirmed correct, %zu "
484
- " pending (%.4f%% of pages, %zu pending)." ,
518
+ " pending (%.4f%% of pages, %zu pending).\n " ,
485
519
correctly_skipped_count_percentage, pending_skipped ().count ,
486
520
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 ());
487
536
}
488
537
489
538
template <size_t kEpochs >
@@ -526,6 +575,9 @@ void FillerStatsTracker<kEpochs>::PrintInPbtxt(PbtxtRegion *hpaa) const {
526
575
absl::ToInt64Milliseconds (absl::Nanoseconds (ts)));
527
576
region.PrintI64 (" min_free_pages" , e.min_free_pages );
528
577
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 ());
529
581
for (int i = 0 ; i < kNumStatsTypes ; i++) {
530
582
auto m = region.CreateSubRegion (labels[i]);
531
583
FillerStats stats = e.stats [i];
@@ -754,13 +806,15 @@ class HugePageFiller {
754
806
// possible hugepage and releasing its free memory to the system. Return the
755
807
// number of pages actually released.
756
808
Length ReleasePages (Length desired,
757
- absl::Duration skip_subrelease_after_peaks_interval)
809
+ absl::Duration skip_subrelease_after_peaks_interval,
810
+ bool hit_limit)
758
811
ABSL_EXCLUSIVE_LOCKS_REQUIRED(pageheap_lock);
759
812
760
813
void AddSpanStats (SmallSpanStats *small, LargeSpanStats *large,
761
814
PageAgeHistograms *ages) const ;
762
815
763
816
BackingStats stats () const ;
817
+ SubreleaseStats subrelease_stats () const { return subrelease_stats_; }
764
818
void Print (TCMalloc_Printer *out, bool everything) const ;
765
819
void PrintInPbtxt (PbtxtRegion *hpaa) const ;
766
820
@@ -842,6 +896,8 @@ class HugePageFiller {
842
896
HugeLength size_;
843
897
};
844
898
899
+ SubreleaseStats subrelease_stats_;
900
+
845
901
// We group hugepages first by longest-free (as a measure of fragmentation),
846
902
// then into 8 chunks inside there by desirability of allocation.
847
903
static constexpr size_t kChunks = 8 ;
@@ -1335,6 +1391,7 @@ inline Length HugePageFiller<TrackerType>::ReleaseCandidates(
1335
1391
absl::c_sort (candidates, CompareForSubrelease);
1336
1392
1337
1393
Length total_released = 0 ;
1394
+ HugeLength total_broken = NHugePages (0 );
1338
1395
#ifndef NDEBUG
1339
1396
Length last = 0 ;
1340
1397
#endif
@@ -1348,6 +1405,12 @@ inline Length HugePageFiller<TrackerType>::ReleaseCandidates(
1348
1405
last = best->used_pages ();
1349
1406
#endif
1350
1407
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
+ }
1351
1414
RemoveFromFillerList (best);
1352
1415
Length ret = best->ReleaseFree ();
1353
1416
unmapped_ += ret;
@@ -1356,6 +1419,15 @@ inline Length HugePageFiller<TrackerType>::ReleaseCandidates(
1356
1419
AddToFillerList (best);
1357
1420
}
1358
1421
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
+ }
1359
1431
return total_released;
1360
1432
}
1361
1433
@@ -1407,7 +1479,8 @@ inline Length HugePageFiller<TrackerType>::GetDesiredSubreleasePages(
1407
1479
// number of pages actually released.
1408
1480
template <class TrackerType >
1409
1481
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) {
1411
1484
Length total_released = 0 ;
1412
1485
1413
1486
// We also do eager release, once we've called this at least once:
@@ -1417,6 +1490,7 @@ inline Length HugePageFiller<TrackerType>::ReleasePages(
1417
1490
// pages.
1418
1491
Length n = unmapping_unaccounted_;
1419
1492
unmapping_unaccounted_ = 0 ;
1493
+ subrelease_stats_.num_pages_subreleased += n;
1420
1494
1421
1495
if (n >= desired) {
1422
1496
return n;
@@ -1433,6 +1507,8 @@ inline Length HugePageFiller<TrackerType>::ReleasePages(
1433
1507
}
1434
1508
}
1435
1509
1510
+ subrelease_stats_.set_limit_hit (hit_limit);
1511
+
1436
1512
// Optimize for releasing up to a huge page worth of small pages (scattered
1437
1513
// over many parts of the filler). Since we hold pageheap_lock, we cannot
1438
1514
// allocate here.
@@ -1693,6 +1769,16 @@ inline void HugePageFiller<TrackerType>::Print(TCMalloc_Printer *out,
1693
1769
nrel.raw_num (), safe_div (unmapped_pages (), nrel.in_pages ()));
1694
1770
out->printf (" HugePageFiller: %.4f of used pages hugepageable\n " ,
1695
1771
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
+
1696
1782
if (!everything) return ;
1697
1783
1698
1784
// Compute some histograms of fullness.
@@ -1722,7 +1808,6 @@ inline void HugePageFiller<TrackerType>::Print(TCMalloc_Printer *out,
1722
1808
1723
1809
out->printf (" \n " );
1724
1810
fillerstats_tracker_.Print (out);
1725
- out->printf (" \n " );
1726
1811
}
1727
1812
1728
1813
template <class TrackerType >
@@ -1759,7 +1844,15 @@ inline void HugePageFiller<TrackerType>::PrintInPbtxt(PbtxtRegion *hpaa) const {
1759
1844
" filler_hugepageable_used_bytes" ,
1760
1845
static_cast <uint64_t >(hugepage_frac () *
1761
1846
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 ());
1763
1856
// Compute some histograms of fullness.
1764
1857
using ::tcmalloc::internal::UsageInfo;
1765
1858
UsageInfo usage;
@@ -1796,7 +1889,10 @@ inline void HugePageFiller<TrackerType>::UpdateFillerStatsTracker() {
1796
1889
n_used_partial_released_ + n_used_released_,
1797
1890
.huge_pages = {regular_alloc_.size (), donated_alloc_.size (),
1798
1891
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 ();
1800
1896
}
1801
1897
1802
1898
template <class TrackerType >
0 commit comments