< prev index next >

src/hotspot/share/gc/shenandoah/shenandoahPacer.cpp

Print this page
rev 59715 : 8247358: Shenandoah: reconsider free budget slice for marking
Reviewed-by: XXX
rev 59716 : 8247367: Shenandoah: pacer should wait on lock instead of exponential backoff
Reviewed-by: XXX


 174 
 175 size_t ShenandoahPacer::update_and_get_progress_history() {
 176   if (_progress == -1) {
 177     // First initialization, report some prior
 178     Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
 179     return (size_t) (_heap->max_capacity() * 0.1);
 180   } else {
 181     // Record history, and reply historical data
 182     _progress_history->add(_progress);
 183     Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
 184     return (size_t) (_progress_history->avg() * HeapWordSize);
 185   }
 186 }
 187 
 188 void ShenandoahPacer::restart_with(size_t non_taxable_bytes, double tax_rate) {
 189   size_t initial = (size_t)(non_taxable_bytes * tax_rate) >> LogHeapWordSize;
 190   STATIC_ASSERT(sizeof(size_t) <= sizeof(intptr_t));
 191   Atomic::xchg(&_budget, (intptr_t)initial);
 192   Atomic::store(&_tax_rate, tax_rate);
 193   Atomic::inc(&_epoch);



 194 }
 195 
 196 bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) {
 197   assert(ShenandoahPacing, "Only be here when pacing is enabled");
 198 
 199   intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
 200 
 201   intptr_t cur = 0;
 202   intptr_t new_val = 0;
 203   do {
 204     cur = Atomic::load(&_budget);
 205     if (cur < tax && !force) {
 206       // Progress depleted, alas.
 207       return false;
 208     }
 209     new_val = cur - tax;
 210   } while (Atomic::cmpxchg(&_budget, cur, new_val) != cur);
 211   return true;
 212 }
 213 
 214 void ShenandoahPacer::unpace_for_alloc(intptr_t epoch, size_t words) {
 215   assert(ShenandoahPacing, "Only be here when pacing is enabled");
 216 
 217   if (_epoch != epoch) {
 218     // Stale ticket, no need to unpace.
 219     return;
 220   }
 221 
 222   intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
 223   Atomic::add(&_budget, tax);
 224 }
 225 
 226 intptr_t ShenandoahPacer::epoch() {
 227   return Atomic::load(&_epoch);
 228 }
 229 
 230 void ShenandoahPacer::pace_for_alloc(size_t words) {
 231   assert(ShenandoahPacing, "Only be here when pacing is enabled");
 232 
 233   // Fast path: try to allocate right away
 234   if (claim_for_alloc(words, false)) {

 235     return;
 236   }
 237 







 238   // Threads that are attaching should not block at all: they are not
 239   // fully initialized yet. Blocking them would be awkward.
 240   // This is probably the path that allocates the thread oop itself.
 241   // Forcefully claim without waiting.
 242   if (JavaThread::current()->is_attaching_via_jni()) {
 243     claim_for_alloc(words, true);
 244     return;
 245   }
 246 
 247   size_t max = ShenandoahPacingMaxDelay;
 248   double start = os::elapsedTime();
 249 
 250   size_t total = 0;
 251   size_t cur = 0;
 252 
 253   while (true) {
 254     // We could instead assist GC, but this would suffice for now.
 255     // This code should also participate in safepointing.
 256     // Perform the exponential backoff, limited by max.
 257 
 258     cur = cur * 2;
 259     if (total + cur > max) {
 260       cur = (max > total) ? (max - total) : 0;
 261     }
 262     cur = MAX2<size_t>(1, cur);
 263 
 264     wait(cur);
 265 
 266     double end = os::elapsedTime();
 267     total = (size_t)((end - start) * 1000);
 268 
 269     if (total > max) {
 270       // Spent local time budget to wait for enough GC progress.
 271       // Breaking out and allocating anyway, which may mean we outpace GC,
 272       // and start Degenerated GC cycle.
 273       _delays.add(total);
 274 
 275       // Forcefully claim the budget: it may go negative at this point, and
 276       // GC should replenish for this and subsequent allocations
 277       claim_for_alloc(words, true);
 278       break;
 279     }
 280 
 281     if (claim_for_alloc(words, false)) {
 282       // Acquired enough permit, nice. Can allocate now.
 283       _delays.add(total);




 284       break;
 285     }
 286   }
 287 }
 288 
 289 void ShenandoahPacer::wait(long time_ms) {
 290   // Perform timed wait. It works like like sleep(), except without modifying
 291   // the thread interruptible status. MonitorLocker also checks for safepoints.
 292   assert(time_ms > 0, "Should not call this with zero argument, as it would stall until notify");
 293   MonitorLocker locker(_wait_monitor);
 294   _wait_monitor->wait(time_ms);
 295 }
 296 
 297 void ShenandoahPacer::notify_waiters() {
 298   MonitorLocker locker(_wait_monitor);
 299   _wait_monitor->notify_all();
 300 }
 301 
 302 void ShenandoahPacer::print_on(outputStream* out) const {
 303   out->print_cr("ALLOCATION PACING:");




 174 
 175 size_t ShenandoahPacer::update_and_get_progress_history() {
 176   if (_progress == -1) {
 177     // First initialization, report some prior
 178     Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
 179     return (size_t) (_heap->max_capacity() * 0.1);
 180   } else {
 181     // Record history, and reply historical data
 182     _progress_history->add(_progress);
 183     Atomic::store(&_progress, (intptr_t)PACING_PROGRESS_ZERO);
 184     return (size_t) (_progress_history->avg() * HeapWordSize);
 185   }
 186 }
 187 
 188 void ShenandoahPacer::restart_with(size_t non_taxable_bytes, double tax_rate) {
 189   size_t initial = (size_t)(non_taxable_bytes * tax_rate) >> LogHeapWordSize;
 190   STATIC_ASSERT(sizeof(size_t) <= sizeof(intptr_t));
 191   Atomic::xchg(&_budget, (intptr_t)initial);
 192   Atomic::store(&_tax_rate, tax_rate);
 193   Atomic::inc(&_epoch);
 194 
 195   // Shake up stalled waiters after budget update.
 196   notify_waiters();
 197 }
 198 
 199 bool ShenandoahPacer::claim_for_alloc(size_t words, bool force) {
 200   assert(ShenandoahPacing, "Only be here when pacing is enabled");
 201 
 202   intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
 203 
 204   intptr_t cur = 0;
 205   intptr_t new_val = 0;
 206   do {
 207     cur = Atomic::load(&_budget);
 208     if (cur < tax && !force) {
 209       // Progress depleted, alas.
 210       return false;
 211     }
 212     new_val = cur - tax;
 213   } while (Atomic::cmpxchg(&_budget, cur, new_val) != cur);
 214   return true;
 215 }
 216 
 217 void ShenandoahPacer::unpace_for_alloc(intptr_t epoch, size_t words) {
 218   assert(ShenandoahPacing, "Only be here when pacing is enabled");
 219 
 220   if (_epoch != epoch) {
 221     // Stale ticket, no need to unpace.
 222     return;
 223   }
 224 
 225   intptr_t tax = MAX2<intptr_t>(1, words * Atomic::load(&_tax_rate));
 226   Atomic::add(&_budget, tax);
 227 }
 228 
 229 intptr_t ShenandoahPacer::epoch() {
 230   return Atomic::load(&_epoch);
 231 }
 232 
 233 void ShenandoahPacer::pace_for_alloc(size_t words) {
 234   assert(ShenandoahPacing, "Only be here when pacing is enabled");
 235 
 236   // Fast path: try to allocate right away
 237   bool claimed = claim_for_alloc(words, false);
 238   if (claimed) {
 239     return;
 240   }
 241 
 242   // Forcefully claim the budget: it may go negative at this point, and
 243   // GC should replenish for this and subsequent allocations. After this claim,
 244   // we would wait a bit until our claim is matched by additional progress,
 245   // or the time budget depletes.
 246   claimed = claim_for_alloc(words, true);
 247   assert(claimed, "Should always succeed");
 248 
 249   // Threads that are attaching should not block at all: they are not
 250   // fully initialized yet. Blocking them would be awkward.
 251   // This is probably the path that allocates the thread oop itself.

 252   if (JavaThread::current()->is_attaching_via_jni()) {

 253     return;
 254   }
 255 

 256   double start = os::elapsedTime();
 257 
 258   size_t max_ms = ShenandoahPacingMaxDelay;
 259   size_t total_ms = 0;
 260 
 261   while (true) {
 262     // We could instead assist GC, but this would suffice for now.
 263     size_t cur_ms = (max_ms > total_ms) ? (max_ms - total_ms) : 1;
 264     wait(cur_ms);








 265 
 266     double end = os::elapsedTime();
 267     total_ms = (size_t)((end - start) * 1000);












 268 
 269     if (total_ms > max_ms || Atomic::load(&_budget) >= 0) {
 270       // Exiting if either:
 271       //  a) Spent local time budget to wait for enough GC progress.
 272       //     Breaking out and allocating anyway, which may mean we outpace GC,
 273       //     and start Degenerated GC cycle.
 274       //  b) The budget had been replenished, which means our claim is satisfied.
 275       _delays.add(total_ms);
 276       break;
 277     }
 278   }
 279 }
 280 
 281 void ShenandoahPacer::wait(long time_ms) {
 282   // Perform timed wait. It works like like sleep(), except without modifying
 283   // the thread interruptible status. MonitorLocker also checks for safepoints.
 284   assert(time_ms > 0, "Should not call this with zero argument, as it would stall until notify");
 285   MonitorLocker locker(_wait_monitor);
 286   _wait_monitor->wait(time_ms);
 287 }
 288 
 289 void ShenandoahPacer::notify_waiters() {
 290   MonitorLocker locker(_wait_monitor);
 291   _wait_monitor->notify_all();
 292 }
 293 
 294 void ShenandoahPacer::print_on(outputStream* out) const {
 295   out->print_cr("ALLOCATION PACING:");


< prev index next >