24
#include "precompiled.hpp"
25
#include "gc/shared/gc_globals.hpp"
26
#include "gc/x/xDirector.hpp"
27
#include "gc/x/xDriver.hpp"
28
#include "gc/x/xHeap.inline.hpp"
29
#include "gc/x/xHeuristics.hpp"
30
#include "gc/x/xStat.hpp"
31
#include "logging/log.hpp"
33
constexpr double one_in_1000 = 3.290527;
34
constexpr double sample_interval = 1.0 / XStatAllocRate::sample_hz;
36
XDirector::XDirector(XDriver* driver) :
38
_metronome(XStatAllocRate::sample_hz) {
39
set_name("XDirector");
43
static void sample_allocation_rate() {
46
const double bytes_per_second = XStatAllocRate::sample_and_reset();
48
log_debug(gc, alloc)("Allocation Rate: %.1fMB/s, Predicted: %.1fMB/s, Avg: %.1f(+/-%.1f)MB/s",
50
XStatAllocRate::predict() / M,
51
XStatAllocRate::avg() / M,
52
XStatAllocRate::sd() / M);
55
static XDriverRequest rule_allocation_stall() {
58
if (!XHeap::heap()->has_alloc_stalled()) {
59
return GCCause::_no_gc;
62
log_debug(gc, director)("Rule: Allocation Stall Observed");
64
return GCCause::_z_allocation_stall;
67
static XDriverRequest rule_warmup() {
68
if (XStatCycle::is_warm()) {
70
return GCCause::_no_gc;
76
const size_t soft_max_capacity = XHeap::heap()->soft_max_capacity();
77
const size_t used = XHeap::heap()->used();
78
const double used_threshold_percent = (XStatCycle::nwarmup_cycles() + 1) * 0.1;
79
const size_t used_threshold = soft_max_capacity * used_threshold_percent;
81
log_debug(gc, director)("Rule: Warmup %.0f%%, Used: " SIZE_FORMAT "MB, UsedThreshold: " SIZE_FORMAT "MB",
82
used_threshold_percent * 100, used / M, used_threshold / M);
84
if (used < used_threshold) {
85
return GCCause::_no_gc;
88
return GCCause::_z_warmup;
91
static XDriverRequest rule_timer() {
92
if (ZCollectionInterval <= 0) {
94
return GCCause::_no_gc;
98
const double time_since_last_gc = XStatCycle::time_since_last();
99
const double time_until_gc = ZCollectionInterval - time_since_last_gc;
101
log_debug(gc, director)("Rule: Timer, Interval: %.3fs, TimeUntilGC: %.3fs",
102
ZCollectionInterval, time_until_gc);
104
if (time_until_gc > 0) {
105
return GCCause::_no_gc;
108
return GCCause::_z_timer;
111
static double estimated_gc_workers(double serial_gc_time, double parallelizable_gc_time, double time_until_deadline) {
112
const double parallelizable_time_until_deadline = MAX2(time_until_deadline - serial_gc_time, 0.001);
113
return parallelizable_gc_time / parallelizable_time_until_deadline;
116
static uint discrete_gc_workers(double gc_workers) {
117
return clamp<uint>(ceil(gc_workers), 1, ConcGCThreads);
120
static double select_gc_workers(double serial_gc_time, double parallelizable_gc_time, double alloc_rate_sd_percent, double time_until_oom) {
122
if (!XStatCycle::is_warm()) {
123
const double not_warm_gc_workers = ConcGCThreads;
124
log_debug(gc, director)("Select GC Workers (Not Warm), GCWorkers: %.3f", not_warm_gc_workers);
125
return not_warm_gc_workers;
129
const double avoid_long_gc_workers = estimated_gc_workers(serial_gc_time, parallelizable_gc_time, 10 );
130
const double avoid_oom_gc_workers = estimated_gc_workers(serial_gc_time, parallelizable_gc_time, time_until_oom);
132
const double gc_workers = MAX2(avoid_long_gc_workers, avoid_oom_gc_workers);
133
const uint actual_gc_workers = discrete_gc_workers(gc_workers);
134
const uint last_gc_workers = XStatCycle::last_active_workers();
137
if (alloc_rate_sd_percent >= 0.15) {
138
const double half_gc_workers = ConcGCThreads / 2.0;
139
const double unsteady_gc_workers = MAX3<double>(gc_workers, last_gc_workers, half_gc_workers);
140
log_debug(gc, director)("Select GC Workers (Unsteady), "
141
"AvoidLongGCWorkers: %.3f, AvoidOOMGCWorkers: %.3f, LastGCWorkers: %.3f, HalfGCWorkers: %.3f, GCWorkers: %.3f",
142
avoid_long_gc_workers, avoid_oom_gc_workers, (double)last_gc_workers, half_gc_workers, unsteady_gc_workers);
143
return unsteady_gc_workers;
146
if (actual_gc_workers < last_gc_workers) {
150
const double gc_duration_delta = (parallelizable_gc_time / actual_gc_workers) - (parallelizable_gc_time / last_gc_workers);
151
const double additional_time_for_allocations = XStatCycle::time_since_last() - gc_duration_delta - sample_interval;
152
const double next_time_until_oom = time_until_oom + additional_time_for_allocations;
153
const double next_avoid_oom_gc_workers = estimated_gc_workers(serial_gc_time, parallelizable_gc_time, next_time_until_oom);
156
const double next_gc_workers = next_avoid_oom_gc_workers + 0.5;
157
const double try_lowering_gc_workers = clamp<double>(next_gc_workers, actual_gc_workers, last_gc_workers);
159
log_debug(gc, director)("Select GC Workers (Try Lowering), "
160
"AvoidLongGCWorkers: %.3f, AvoidOOMGCWorkers: %.3f, NextAvoidOOMGCWorkers: %.3f, LastGCWorkers: %.3f, GCWorkers: %.3f",
161
avoid_long_gc_workers, avoid_oom_gc_workers, next_avoid_oom_gc_workers, (double)last_gc_workers, try_lowering_gc_workers);
162
return try_lowering_gc_workers;
165
log_debug(gc, director)("Select GC Workers (Normal), "
166
"AvoidLongGCWorkers: %.3f, AvoidOOMGCWorkers: %.3f, LastGCWorkers: %.3f, GCWorkers: %.3f",
167
avoid_long_gc_workers, avoid_oom_gc_workers, (double)last_gc_workers, gc_workers);
171
static XDriverRequest rule_allocation_rate_dynamic() {
172
if (!XStatCycle::is_time_trustable()) {
174
return GCCause::_no_gc;
179
const size_t soft_max_capacity = XHeap::heap()->soft_max_capacity();
180
const size_t used = XHeap::heap()->used();
181
const size_t free_including_headroom = soft_max_capacity - MIN2(soft_max_capacity, used);
182
const size_t free = free_including_headroom - MIN2(free_including_headroom, XHeuristics::relocation_headroom());
190
const double alloc_rate_predict = XStatAllocRate::predict();
191
const double alloc_rate_avg = XStatAllocRate::avg();
192
const double alloc_rate_sd = XStatAllocRate::sd();
193
const double alloc_rate_sd_percent = alloc_rate_sd / (alloc_rate_avg + 1.0);
194
const double alloc_rate = (MAX2(alloc_rate_predict, alloc_rate_avg) * ZAllocationSpikeTolerance) + (alloc_rate_sd * one_in_1000) + 1.0;
195
const double time_until_oom = (free / alloc_rate) / (1.0 + alloc_rate_sd_percent);
199
const double serial_gc_time = XStatCycle::serial_time().davg() + (XStatCycle::serial_time().dsd() * one_in_1000);
200
const double parallelizable_gc_time = XStatCycle::parallelizable_time().davg() + (XStatCycle::parallelizable_time().dsd() * one_in_1000);
203
const double gc_workers = select_gc_workers(serial_gc_time, parallelizable_gc_time, alloc_rate_sd_percent, time_until_oom);
206
const uint actual_gc_workers = discrete_gc_workers(gc_workers);
209
const double actual_gc_duration = serial_gc_time + (parallelizable_gc_time / actual_gc_workers);
210
const uint last_gc_workers = XStatCycle::last_active_workers();
215
const double time_until_gc = time_until_oom - actual_gc_duration - sample_interval;
217
log_debug(gc, director)("Rule: Allocation Rate (Dynamic GC Workers), "
218
"MaxAllocRate: %.1fMB/s (+/-%.1f%%), Free: " SIZE_FORMAT "MB, GCCPUTime: %.3f, "
219
"GCDuration: %.3fs, TimeUntilOOM: %.3fs, TimeUntilGC: %.3fs, GCWorkers: %u -> %u",
221
alloc_rate_sd_percent * 100,
223
serial_gc_time + parallelizable_gc_time,
224
serial_gc_time + (parallelizable_gc_time / actual_gc_workers),
230
if (actual_gc_workers <= last_gc_workers && time_until_gc > 0) {
231
return XDriverRequest(GCCause::_no_gc, actual_gc_workers);
234
return XDriverRequest(GCCause::_z_allocation_rate, actual_gc_workers);
237
static XDriverRequest rule_allocation_rate_static() {
238
if (!XStatCycle::is_time_trustable()) {
240
return GCCause::_no_gc;
251
const size_t soft_max_capacity = XHeap::heap()->soft_max_capacity();
252
const size_t used = XHeap::heap()->used();
253
const size_t free_including_headroom = soft_max_capacity - MIN2(soft_max_capacity, used);
254
const size_t free = free_including_headroom - MIN2(free_including_headroom, XHeuristics::relocation_headroom());
262
const double max_alloc_rate = (XStatAllocRate::avg() * ZAllocationSpikeTolerance) + (XStatAllocRate::sd() * one_in_1000);
263
const double time_until_oom = free / (max_alloc_rate + 1.0);
267
const double serial_gc_time = XStatCycle::serial_time().davg() + (XStatCycle::serial_time().dsd() * one_in_1000);
268
const double parallelizable_gc_time = XStatCycle::parallelizable_time().davg() + (XStatCycle::parallelizable_time().dsd() * one_in_1000);
271
const double gc_duration = serial_gc_time + (parallelizable_gc_time / ConcGCThreads);
276
const double time_until_gc = time_until_oom - gc_duration - sample_interval;
278
log_debug(gc, director)("Rule: Allocation Rate (Static GC Workers), MaxAllocRate: %.1fMB/s, Free: " SIZE_FORMAT "MB, GCDuration: %.3fs, TimeUntilGC: %.3fs",
279
max_alloc_rate / M, free / M, gc_duration, time_until_gc);
281
if (time_until_gc > 0) {
282
return GCCause::_no_gc;
285
return GCCause::_z_allocation_rate;
288
static XDriverRequest rule_allocation_rate() {
289
if (UseDynamicNumberOfGCThreads) {
290
return rule_allocation_rate_dynamic();
292
return rule_allocation_rate_static();
296
static XDriverRequest rule_high_usage() {
305
const size_t soft_max_capacity = XHeap::heap()->soft_max_capacity();
306
const size_t used = XHeap::heap()->used();
307
const size_t free_including_headroom = soft_max_capacity - MIN2(soft_max_capacity, used);
308
const size_t free = free_including_headroom - MIN2(free_including_headroom, XHeuristics::relocation_headroom());
309
const double free_percent = percent_of(free, soft_max_capacity);
311
log_debug(gc, director)("Rule: High Usage, Free: " SIZE_FORMAT "MB(%.1f%%)",
312
free / M, free_percent);
314
if (free_percent > 5.0) {
315
return GCCause::_no_gc;
318
return GCCause::_z_high_usage;
321
static XDriverRequest rule_proactive() {
322
if (!ZProactive || !XStatCycle::is_warm()) {
324
return GCCause::_no_gc;
336
const size_t used_after_last_gc = XStatHeap::used_at_relocate_end();
337
const size_t used_increase_threshold = XHeap::heap()->soft_max_capacity() * 0.10;
338
const size_t used_threshold = used_after_last_gc + used_increase_threshold;
339
const size_t used = XHeap::heap()->used();
340
const double time_since_last_gc = XStatCycle::time_since_last();
341
const double time_since_last_gc_threshold = 5 * 60;
342
if (used < used_threshold && time_since_last_gc < time_since_last_gc_threshold) {
344
log_debug(gc, director)("Rule: Proactive, UsedUntilEnabled: " SIZE_FORMAT "MB, TimeUntilEnabled: %.3fs",
345
(used_threshold - used) / M,
346
time_since_last_gc_threshold - time_since_last_gc);
347
return GCCause::_no_gc;
350
const double assumed_throughput_drop_during_gc = 0.50;
351
const double acceptable_throughput_drop = 0.01;
352
const double serial_gc_time = XStatCycle::serial_time().davg() + (XStatCycle::serial_time().dsd() * one_in_1000);
353
const double parallelizable_gc_time = XStatCycle::parallelizable_time().davg() + (XStatCycle::parallelizable_time().dsd() * one_in_1000);
354
const double gc_duration = serial_gc_time + (parallelizable_gc_time / ConcGCThreads);
355
const double acceptable_gc_interval = gc_duration * ((assumed_throughput_drop_during_gc / acceptable_throughput_drop) - 1.0);
356
const double time_until_gc = acceptable_gc_interval - time_since_last_gc;
358
log_debug(gc, director)("Rule: Proactive, AcceptableGCInterval: %.3fs, TimeSinceLastGC: %.3fs, TimeUntilGC: %.3fs",
359
acceptable_gc_interval, time_since_last_gc, time_until_gc);
361
if (time_until_gc > 0) {
362
return GCCause::_no_gc;
365
return GCCause::_z_proactive;
368
static XDriverRequest make_gc_decision() {
370
using XDirectorRule = XDriverRequest (*)();
371
const XDirectorRule rules[] = {
372
rule_allocation_stall,
375
rule_allocation_rate,
381
for (size_t i = 0; i < ARRAY_SIZE(rules); i++) {
382
const XDriverRequest request = rules[i]();
383
if (request.cause() != GCCause::_no_gc) {
388
return GCCause::_no_gc;
391
void XDirector::run_service() {
393
while (_metronome.wait_for_tick()) {
394
sample_allocation_rate();
395
if (!_driver->is_busy()) {
396
const XDriverRequest request = make_gc_decision();
397
if (request.cause() != GCCause::_no_gc) {
398
_driver->collect(request);
404
void XDirector::stop_service() {