1 /*
  2  * Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  *
 23  */
 24 
 25 #include "precompiled.hpp"
 26 
 27 #include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp"
 28 #include "gc/shenandoah/shenandoahAgeCensus.hpp"
 29 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 30 
 31 ShenandoahAgeCensus::ShenandoahAgeCensus() {
 32   assert(ShenandoahHeap::heap()->mode()->is_generational(), "Only in generational mode");
 33 
 34   _global_age_table = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC);
 35   CENSUS_NOISE(_global_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, MAX_SNAPSHOTS, mtGC);)
 36   _tenuring_threshold = NEW_C_HEAP_ARRAY(uint, MAX_SNAPSHOTS, mtGC);
 37 
 38   for (int i = 0; i < MAX_SNAPSHOTS; i++) {
 39     // Note that we don't now get perfdata from age_table
 40     _global_age_table[i] = new AgeTable(false);
 41     CENSUS_NOISE(_global_noise[i].clear();)
 42     // Sentinel value
 43     _tenuring_threshold[i] = MAX_COHORTS;
 44   }
 45   if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
 46     size_t max_workers = ShenandoahHeap::heap()->max_workers();
 47     _local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, max_workers, mtGC);
 48     CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);)
 49     for (uint i = 0; i < max_workers; i++) {
 50       _local_age_table[i] = new AgeTable(false);
 51       CENSUS_NOISE(_local_noise[i].clear();)
 52     }
 53   } else {
 54     _local_age_table = nullptr;
 55   }
 56   _epoch = MAX_SNAPSHOTS - 1;  // see update_epoch()
 57 }
 58 
 59 CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, uint region_youth, size_t size, uint worker_id) {)
 60 NO_CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, size_t size, uint worker_id) {)
 61   if (obj_age <= markWord::max_age) {
 62     assert(obj_age < MAX_COHORTS && region_age < MAX_COHORTS, "Should have been tenured");
 63 #ifdef SHENANDOAH_CENSUS_NOISE
 64     // Region ageing is stochastic and non-monotonic; this vitiates mortality
 65     // demographics in ways that might defeat our algorithms. Marking may be a
 66     // time when we might be able to correct this, but we currently do not do
 67     // this. Like skipped statistics further below, we want to track the
 68     // impact of this noise to see if this may be worthwhile. JDK-<TBD>.
 69     uint age = obj_age;
 70     if (region_age > 0) {
 71       add_aged(size, worker_id);   // this tracking is coarse for now
 72       age += region_age;
 73       if (age >= MAX_COHORTS) {
 74         age = (uint)(MAX_COHORTS - 1);  // clamp
 75         add_clamped(size, worker_id);
 76       }
 77     }
 78     if (region_youth > 0) {   // track object volume with retrograde age
 79       add_young(size, worker_id);
 80     }
 81 #else   // SHENANDOAH_CENSUS_NOISE
 82     uint age = MIN2(obj_age + region_age, (uint)(MAX_COHORTS - 1));  // clamp
 83 #endif  // SHENANDOAH_CENSUS_NOISE
 84     get_local_age_table(worker_id)->add(age, size);
 85   } else {
 86     // update skipped statistics
 87     CENSUS_NOISE(add_skipped(size, worker_id);)
 88   }
 89 }
 90 
 91 #ifdef SHENANDOAH_CENSUS_NOISE
 92 void ShenandoahAgeCensus::add_skipped(size_t size, uint worker_id) {
 93   _local_noise[worker_id].skipped += size;
 94 }
 95 
 96 void ShenandoahAgeCensus::add_aged(size_t size, uint worker_id) {
 97   _local_noise[worker_id].aged += size;
 98 }
 99 
100 void ShenandoahAgeCensus::add_clamped(size_t size, uint worker_id) {
101   _local_noise[worker_id].clamped += size;
102 }
103 
104 void ShenandoahAgeCensus::add_young(size_t size, uint worker_id) {
105   _local_noise[worker_id].young += size;
106 }
107 #endif // SHENANDOAH_CENSUS_NOISE
108 
109 // Prepare for a new census update, by clearing appropriate global slots.
110 void ShenandoahAgeCensus::prepare_for_census_update() {
111   assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
112   if (++_epoch >= MAX_SNAPSHOTS) {
113     _epoch=0;
114   }
115   _global_age_table[_epoch]->clear();
116   CENSUS_NOISE(_global_noise[_epoch].clear();)
117 }
118 
119 // Update the census data from appropriate sources,
120 // and compute the new tenuring threshold.
121 void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable* pv2) {
122   // Check that we won't overwrite existing data: caller is
123   // responsible for explicitly clearing the slot via calling
124   // prepare_for_census_update().
125   assert(_global_age_table[_epoch]->is_clear(), "Dirty decks");
126   CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");)
127   if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) {
128     assert(pv1 == nullptr && pv2 == nullptr, "Error, check caller");
129     // Seed cohort 0 with population that may have been missed during
130     // regular census.
131     _global_age_table[_epoch]->add((uint)0, age0_pop);
132 
133     size_t max_workers = ShenandoahHeap::heap()->max_workers();
134     // Merge data from local age tables into the global age table for the epoch,
135     // clearing the local tables.
136     for (uint i = 0; i < max_workers; i++) {
137       // age stats
138       _global_age_table[_epoch]->merge(_local_age_table[i]);
139       _local_age_table[i]->clear();   // clear for next census
140       // Merge noise stats
141       CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);)
142       CENSUS_NOISE(_local_noise[i].clear();)
143     }
144   } else {
145     // census during evac
146     assert(pv1 != nullptr && pv2 != nullptr, "Error, check caller");
147     _global_age_table[_epoch]->merge(pv1);
148     _global_age_table[_epoch]->merge(pv2);
149   }
150 
151   update_tenuring_threshold();
152 }
153 
154 
155 // Reset the epoch for the global age tables,
156 // clearing all history.
157 void ShenandoahAgeCensus::reset_global() {
158   assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
159   for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
160     _global_age_table[i]->clear();
161     CENSUS_NOISE(_global_noise[i].clear();)
162   }
163   _epoch = MAX_SNAPSHOTS;
164   assert(_epoch < MAX_SNAPSHOTS, "Error");
165 }
166 
167 // Reset the local age tables, clearing any partial census.
168 void ShenandoahAgeCensus::reset_local() {
169   if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) {
170     assert(_local_age_table == nullptr, "Error");
171     return;
172   }
173   size_t max_workers = ShenandoahHeap::heap()->max_workers();
174   for (uint i = 0; i < max_workers; i++) {
175     _local_age_table[i]->clear();
176     CENSUS_NOISE(_local_noise[i].clear();)
177   }
178 }
179 
180 // Is global census information clear?
181 bool ShenandoahAgeCensus::is_clear_global() {
182   assert(_epoch < MAX_SNAPSHOTS, "Out of bounds");
183   for (uint i = 0; i < MAX_SNAPSHOTS; i++) {
184     bool clear = _global_age_table[i]->is_clear();
185     CENSUS_NOISE(clear |= _global_noise[i].is_clear();)
186     if (!clear) {
187       return false;
188     }
189   }
190   return true;
191 }
192 
193 // Is local census information clear?
194 bool ShenandoahAgeCensus::is_clear_local() {
195   if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) {
196     assert(_local_age_table == nullptr, "Error");
197     return true;
198   }
199   size_t max_workers = ShenandoahHeap::heap()->max_workers();
200   for (uint i = 0; i < max_workers; i++) {
201     bool clear = _local_age_table[i]->is_clear();
202     CENSUS_NOISE(clear |= _local_noise[i].is_clear();)
203     if (!clear) {
204       return false;
205     }
206   }
207   return true;
208 }
209 
210 void ShenandoahAgeCensus::update_tenuring_threshold() {
211   if (!ShenandoahGenerationalAdaptiveTenuring) {
212     _tenuring_threshold[_epoch] = InitialTenuringThreshold;
213   } else {
214     uint tt = compute_tenuring_threshold();
215     assert(tt <= MAX_COHORTS, "Out of bounds");
216     _tenuring_threshold[_epoch] = tt;
217   }
218   print();
219   log_trace(gc, age)("New tenuring threshold " UINTX_FORMAT " (min " UINTX_FORMAT ", max " UINTX_FORMAT")",
220     (uintx) _tenuring_threshold[_epoch], ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge);
221 }
222 
223 uint ShenandoahAgeCensus::compute_tenuring_threshold() {
224   // Starting with the oldest cohort with a non-trivial population
225   // (as specified by ShenandoahGenerationalTenuringCohortPopulationThreshold) in the
226   // previous epoch, and working down the cohorts by age, find the
227   // oldest age that has a significant mortality rate (as specified by
228   // ShenandoahGenerationalTenuringMortalityRateThreshold). We use this as
229   // tenuring age to be used for the evacuation cycle to follow.
230   // Results are clamped between user-specified min & max guardrails,
231   // so we ignore any cohorts outside ShenandoahGenerational[Min,Max]Age.
232 
233   // Current and previous epoch in ring
234   const uint cur_epoch = _epoch;
235   const uint prev_epoch = cur_epoch > 0  ? cur_epoch - 1 : markWord::max_age;
236 
237   // Current and previous population vectors in ring
238   const AgeTable* cur_pv = _global_age_table[cur_epoch];
239   const AgeTable* prev_pv = _global_age_table[prev_epoch];
240   uint upper_bound = ShenandoahGenerationalMaxTenuringAge - 1;
241   const uint prev_tt = previous_tenuring_threshold();
242   if (ShenandoahGenerationalCensusIgnoreOlderCohorts && prev_tt > 0) {
243      // We stay below the computed tenuring threshold for the last cycle plus 1,
244      // ignoring the mortality rates of any older cohorts.
245      upper_bound = MIN2(upper_bound, prev_tt + 1);
246   }
247   uint tenuring_threshold = upper_bound;
248   for (uint i = upper_bound; i > MAX2((uint)ShenandoahGenerationalMinTenuringAge, (uint)0); i--) {
249     assert(i > 0, "Index (i-1) would underflow/wrap");
250     // Cohort of current age i
251     const size_t cur_pop = cur_pv->sizes[i];
252     const size_t prev_pop = prev_pv->sizes[i-1];
253     const double mr = mortality_rate(prev_pop, cur_pop);
254     // We ignore any cohorts that had a very low population count, or
255     // that have a lower mortality rate than we care to age in young; these
256     // cohorts are considered eligible for tenuring when all older
257     // cohorts are.
258     if (prev_pop < ShenandoahGenerationalTenuringCohortPopulationThreshold ||
259         mr < ShenandoahGenerationalTenuringMortalityRateThreshold) {
260       tenuring_threshold = i;
261       continue;
262     }
263     return tenuring_threshold;
264   }
265   return tenuring_threshold;
266 }
267 
268 // Mortality rate of a cohort, given its previous and current population
269 double ShenandoahAgeCensus::mortality_rate(size_t prev_pop, size_t cur_pop) {
270   // The following also covers the case where both entries are 0
271   if (prev_pop <= cur_pop) {
272     // adjust for inaccurate censuses by finessing the
273     // reappearance of dark matter as normal matter;
274     // mortality rate is 0 if population remained the same
275     // or increased.
276     if (cur_pop > prev_pop) {
277       log_trace(gc, age)
278         (" (dark matter) Cohort population " SIZE_FORMAT_W(10) " to " SIZE_FORMAT_W(10),
279         prev_pop*oopSize, cur_pop*oopSize);
280     }
281     return 0.0;
282   }
283   assert(prev_pop > 0 && prev_pop > cur_pop, "Error");
284   return 1.0 - (((double)cur_pop)/((double)prev_pop));
285 }
286 
287 void ShenandoahAgeCensus::print() {
288   // Print the population vector for the current epoch, and
289   // for the previous epoch, as well as the computed mortality
290   // ratio for each extant cohort.
291   const uint cur_epoch = _epoch;
292   const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1: markWord::max_age;
293 
294   const AgeTable* cur_pv = _global_age_table[cur_epoch];
295   const AgeTable* prev_pv = _global_age_table[prev_epoch];
296 
297   const uint tt = tenuring_threshold();
298 
299   size_t total= 0;
300   for (uint i = 1; i < MAX_COHORTS; i++) {
301     const size_t prev_pop = prev_pv->sizes[i-1];  // (i-1) OK because i >= 1
302     const size_t cur_pop  = cur_pv->sizes[i];
303     double mr = mortality_rate(prev_pop, cur_pop);
304     // Suppress printing when everything is zero
305     if (prev_pop + cur_pop > 0) {
306       log_info(gc, age)
307         (" - age %3u: prev " SIZE_FORMAT_W(10) " bytes, curr " SIZE_FORMAT_W(10) " bytes, mortality %.2f ",
308          i, prev_pop*oopSize, cur_pop*oopSize, mr);
309     }
310     total += cur_pop;
311     if (i == tt) {
312       // Underline the cohort for tenuring threshold (if < MAX_COHORTS)
313       log_info(gc, age)("----------------------------------------------------------------------------");
314     }
315   }
316   CENSUS_NOISE(_global_noise[cur_epoch].print(total);)
317 }
318 
319 #ifdef SHENANDOAH_CENSUS_NOISE
320 void ShenandoahNoiseStats::print(size_t total) {
321   if (total > 0) {
322     float f_skipped = (float)skipped/(float)total;
323     float f_aged    = (float)aged/(float)total;
324     float f_clamped = (float)clamped/(float)total;
325     float f_young   = (float)young/(float)total;
326     log_info(gc, age)("Skipped: " SIZE_FORMAT_W(10) " (%.2f),  R-Aged: " SIZE_FORMAT_W(10) " (%.2f),  "
327                       "Clamped: " SIZE_FORMAT_W(10) " (%.2f),  R-Young: " SIZE_FORMAT_W(10) " (%.2f)",
328                       skipped*oopSize, f_skipped, aged*oopSize, f_aged,
329                       clamped*oopSize, f_clamped, young*oopSize, f_young);
330   }
331 }
332 #endif // SHENANDOAH_CENSUS_NOISE