1 /* 2 * Copyright (c) 2015, Oracle and/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 #include "gc/g1/g1CollectedHeap.inline.hpp" 27 #include "gc/g1/g1ErgoVerbose.hpp" 28 #include "gc/g1/g1IHOPControl.hpp" 29 #include "gc/g1/g1Predictions.hpp" 30 31 G1IHOPControl::G1IHOPControl(double initial_ihop_percent, size_t target_occupancy) : 32 _initial_ihop_percent(initial_ihop_percent), 33 _target_occupancy(target_occupancy) { 34 assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0, "Initial IHOP value must be between 0 and 100 but is %.3f", initial_ihop_percent); 35 } 36 37 G1StaticIHOPControl::G1StaticIHOPControl(double ihop_percent, size_t target_occupancy) : 38 G1IHOPControl(ihop_percent, target_occupancy), 39 _last_allocation_time_s(0.0), 40 _last_allocated_bytes(0), 41 _last_marking_length_s(0.0) { 42 assert(_target_occupancy > 0, "Target occupancy must be larger than zero."); 43 } 44 45 void G1StaticIHOPControl::print() { 46 ergo_verbose6(ErgoIHOP, 47 "basic information", 48 ergo_format_reason("value update") 49 ergo_format_byte_perc("threshold") 50 ergo_format_byte("target occupancy") 51 ergo_format_byte("current occupancy") 52 ergo_format_double("recent old gen allocation rate") 53 ergo_format_double("recent marking phase length"), 54 get_conc_mark_start_threshold(), 55 _initial_ihop_percent, 56 _target_occupancy, 57 G1CollectedHeap::heap()->used(), 58 _last_allocation_time_s > 0.0 ? _last_allocated_bytes / _last_allocation_time_s : 0.0, 59 _last_marking_length_s); 60 } 61 62 #ifndef PRODUCT 63 static void test_update(G1IHOPControl* ctrl, double alloc_time, size_t alloc_amount, size_t young_size, double mark_time) { 64 for (int i = 0; i < 100; i++) { 65 ctrl->update_allocation_info(alloc_time, alloc_amount, young_size); 66 ctrl->update_marking_length(mark_time); 67 } 68 } 69 70 void G1StaticIHOPControl::test() { 71 size_t const initial_ihop = 45; 72 73 G1StaticIHOPControl ctrl(initial_ihop, 100); 74 size_t threshold; 75 76 threshold = ctrl.get_conc_mark_start_threshold(); 77 assert(threshold == initial_ihop, 78 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 79 80 ctrl.update_allocation_info(100.0, 100, 100); 81 threshold = ctrl.get_conc_mark_start_threshold(); 82 assert(threshold == initial_ihop, 83 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 84 85 ctrl.update_marking_length(1000.0); 86 threshold = ctrl.get_conc_mark_start_threshold(); 87 assert(threshold == initial_ihop, 88 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 89 90 // Whatever we pass, the IHOP value must stay the same. 91 test_update(&ctrl, 2, 10, 10, 3); 92 threshold = ctrl.get_conc_mark_start_threshold(); 93 assert(threshold == initial_ihop, 94 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 95 96 test_update(&ctrl, 12, 10, 10, 3); 97 threshold = ctrl.get_conc_mark_start_threshold(); 98 assert(threshold == initial_ihop, 99 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_ihop, threshold); 100 } 101 #endif 102 103 G1AdaptiveIHOPControl::G1AdaptiveIHOPControl(double ihop_percent, 104 size_t initial_target_occupancy, 105 G1Predictions const* predictor, 106 size_t heap_reserve_percent, 107 size_t heap_waste_percent) : 108 G1IHOPControl(ihop_percent, initial_target_occupancy), 109 _predictor(predictor), 110 _marking_times_s(10, 0.95), 111 _allocation_rate_s(10, 0.95), 112 _last_unrestrained_young_size(0), 113 _current_threshold(0.0), 114 _heap_reserve_percent(heap_reserve_percent), 115 _heap_waste_percent(heap_waste_percent) 116 { 117 calculate(); 118 } 119 120 void G1AdaptiveIHOPControl::calculate() { 121 if (have_enough_data_for_prediction()) { 122 double pred_marking_time = _predictor->get_new_prediction(&_marking_times_s); 123 double pred_promotion_rate = _predictor->get_new_prediction(&_allocation_rate_s); 124 125 size_t predicted_needed_bytes_during_marking = 126 (pred_marking_time * pred_promotion_rate + 127 _last_unrestrained_young_size); // In reality we would need the maximum size of the young gen during marking. This is a conservative estimate. 128 129 size_t internal_threshold = actual_target_threshold(); 130 size_t predicted_initiating_threshold = predicted_needed_bytes_during_marking < internal_threshold ? 131 internal_threshold - predicted_needed_bytes_during_marking : 132 0; 133 _current_threshold = predicted_initiating_threshold; 134 } else { 135 // Use the initial value. 136 _current_threshold = _initial_ihop_percent * _target_occupancy / 100.0; 137 } 138 } 139 140 size_t G1AdaptiveIHOPControl::actual_target_threshold() const { 141 // The actual target threshold takes the heap reserve and the expected waste in 142 // free space into account. 143 // _heap_reserve is that part of the total heap capacity that is reserved for 144 // eventual promotion failure. 145 // _heap_waste is the amount of space will never be reclaimed in any 146 // heap, so can not be used for allocation during marking and must always be 147 // considered. 148 149 double safe_total_heap_percentage = MIN2((double)(_heap_reserve_percent + _heap_waste_percent), 100.0); 150 151 return MIN2( 152 G1CollectedHeap::heap()->max_capacity() * (100.0 - safe_total_heap_percentage) / 100.0, 153 _target_occupancy * (100.0 - _heap_waste_percent) / 100.0 154 ); 155 } 156 157 bool G1AdaptiveIHOPControl::have_enough_data_for_prediction() const { 158 return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) && 159 ((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples); 160 } 161 162 void G1AdaptiveIHOPControl::set_target_occupancy(size_t target_occupancy) { 163 _target_occupancy = target_occupancy; 164 } 165 166 size_t G1AdaptiveIHOPControl::get_conc_mark_start_threshold() { 167 calculate(); 168 return _current_threshold; 169 } 170 171 void G1AdaptiveIHOPControl::update_allocation_info(double allocation_time_s, size_t allocated_bytes, size_t additional_buffer_size) { 172 assert(allocation_time_s >= 0.0, "Allocation time must be positive but is %.3f", allocation_time_s); 173 double allocation_rate = (double) allocated_bytes / allocation_time_s; 174 _allocation_rate_s.add(allocation_rate); 175 176 _last_allocation_bytes = allocated_bytes; 177 _last_unrestrained_young_size = additional_buffer_size; 178 } 179 180 void G1AdaptiveIHOPControl::update_marking_length(double marking_length_s) { 181 assert(marking_length_s >= 0.0, "Marking length must be larger than zero but is %.3f", marking_length_s); 182 _marking_times_s.add(marking_length_s); 183 } 184 185 static double percentage_of(double quantity, double base_quantity) { 186 return base_quantity > 0.0 ? (quantity / base_quantity) * 100.0 : 0.0; 187 } 188 189 void G1AdaptiveIHOPControl::print() { 190 ergo_verbose6(ErgoIHOP, 191 "basic information", 192 ergo_format_reason("value update") 193 ergo_format_byte_perc("threshold") 194 ergo_format_byte("target occupancy") 195 ergo_format_byte("current occupancy") 196 ergo_format_double("recent old gen allocation rate") 197 ergo_format_double("recent marking phase length"), 198 get_conc_mark_start_threshold(), 199 percentage_of(get_conc_mark_start_threshold(), _target_occupancy), 200 _target_occupancy, 201 G1CollectedHeap::heap()->used(), 202 _allocation_rate_s.last(), 203 _marking_times_s.last() 204 ); 205 size_t actual_target = actual_target_threshold(); 206 ergo_verbose6(ErgoIHOP, 207 "adaptive IHOP information", 208 ergo_format_reason("value update") 209 ergo_format_byte_perc("threshold") 210 ergo_format_byte("internal target occupancy") 211 ergo_format_double("predicted old gen allocation rate") 212 ergo_format_double("predicted marking phase length") 213 ergo_format_str("prediction active"), 214 get_conc_mark_start_threshold(), 215 percentage_of(get_conc_mark_start_threshold(), actual_target), 216 actual_target, 217 _predictor->get_new_prediction(&_allocation_rate_s), 218 _predictor->get_new_prediction(&_marking_times_s), 219 have_enough_data_for_prediction() ? "true" : "false" 220 ); 221 } 222 223 #ifndef PRODUCT 224 void G1AdaptiveIHOPControl::test() { 225 size_t const initial_threshold = 45; 226 size_t const young_size = 10; 227 size_t const target_size = 100; 228 229 // The final IHOP value is always 230 // target_size - (young_size + alloc_amount/alloc_time * marking_time) 231 232 G1Predictions pred(0.95); 233 G1AdaptiveIHOPControl ctrl(initial_threshold, target_size, &pred, 0, 0); 234 235 // First "load". 236 size_t const alloc_time1 = 2; 237 size_t const alloc_amount1 = 10; 238 size_t const marking_time1 = 2; 239 size_t const settled_ihop1 = target_size - (young_size + alloc_amount1/alloc_time1 * marking_time1); 240 241 size_t threshold; 242 threshold = ctrl.get_conc_mark_start_threshold(); 243 assert(threshold == initial_threshold, 244 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 245 for (size_t i = 0; i < G1AdaptiveIHOPNumInitialSamples - 1; i++) { 246 ctrl.update_allocation_info(alloc_time1, alloc_amount1, young_size); 247 ctrl.update_marking_length(marking_time1); 248 // Not enough data yet. 249 threshold = ctrl.get_conc_mark_start_threshold(); 250 assert(threshold == initial_threshold, 251 "Expected IHOP threshold of " SIZE_FORMAT " but is " SIZE_FORMAT, initial_threshold, threshold); 252 } 253 254 test_update(&ctrl, alloc_time1, alloc_amount1, young_size, marking_time1); 255 256 threshold = ctrl.get_conc_mark_start_threshold(); 257 assert(threshold == settled_ihop1, 258 "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); 259 260 // Second "load". A bit higher allocation rate. 261 size_t const alloc_time2 = 2; 262 size_t const alloc_amount2 = 30; 263 size_t const marking_time2 = 2; 264 size_t const settled_ihop2 = target_size - (young_size + alloc_amount2/alloc_time2 * marking_time2); 265 266 test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); 267 268 threshold = ctrl.get_conc_mark_start_threshold(); 269 assert(threshold < settled_ihop1, 270 "Expected IHOP threshold to settle at a value lower than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop1, threshold); 271 272 // Third "load". Very high (impossible) allocation rate. 273 size_t const alloc_time3 = 1; 274 size_t const alloc_amount3 = 50; 275 size_t const marking_time3 = 2; 276 size_t const settled_ihop3 = 0; 277 278 test_update(&ctrl, alloc_time3, alloc_amount3, young_size, marking_time3); 279 threshold = ctrl.get_conc_mark_start_threshold(); 280 281 assert(threshold == settled_ihop3, 282 "Expected IHOP threshold to settle at " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); 283 284 // And back to some arbitrary value. 285 test_update(&ctrl, alloc_time2, alloc_amount2, young_size, marking_time2); 286 287 threshold = ctrl.get_conc_mark_start_threshold(); 288 assert(threshold > settled_ihop3, 289 "Expected IHOP threshold to settle at value larger than " SIZE_FORMAT " but is " SIZE_FORMAT, settled_ihop3, threshold); 290 } 291 292 void IHOP_test() { 293 G1StaticIHOPControl::test(); 294 G1AdaptiveIHOPControl::test(); 295 } 296 #endif