blob: 8767f8e1da99baaee59dd4896d77f0c56c303101 [file] [log] [blame]
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "gc/g1/g1Analytics.hpp"
#include "gc/g1/g1ConcurrentRefineThreadsNeeded.hpp"
#include "gc/g1/heapRegion.hpp"
#include "gc/g1/g1Policy.hpp"
#include "utilities/globalDefinitions.hpp"
#include <math.h>
G1ConcurrentRefineThreadsNeeded::G1ConcurrentRefineThreadsNeeded(G1Policy* policy,
double update_period_ms) :
_policy(policy),
_update_period_ms(update_period_ms),
_predicted_time_until_next_gc_ms(0.0),
_predicted_cards_at_next_gc(0),
_threads_needed(0)
{}
// Estimate how many concurrent refinement threads we need to run to achieve
// the target number of card by the time the next GC happens. There are
// several secondary goals we'd like to achieve while meeting that goal.
//
// 1. Minimize the number of refinement threads running at once.
//
// 2. Minimize the number of activations and deactivations for the
// refinement threads that run.
//
// 3. Delay performing refinement work. Having more dirty cards waiting to
// be refined can be beneficial, as further writes to the same card don't
// create more work.
void G1ConcurrentRefineThreadsNeeded::update(uint active_threads,
size_t available_bytes,
size_t num_cards,
size_t target_num_cards) {
const G1Analytics* analytics = _policy->analytics();
// Estimate time until next GC, based on remaining bytes available for
// allocation and the allocation rate.
double alloc_region_rate = analytics->predict_alloc_rate_ms();
double alloc_bytes_rate = alloc_region_rate * HeapRegion::GrainBytes;
if (alloc_bytes_rate == 0.0) {
// A zero rate indicates we don't yet have data to use for predictions.
// Since we don't have any idea how long until the next GC, use a time of
// zero.
_predicted_time_until_next_gc_ms = 0.0;
} else {
// If the heap size is large and the allocation rate is small, we can get
// a predicted time until next GC that is so large it can cause problems
// (such as overflow) in other calculations. Limit the prediction to one
// hour, which is still large in this context.
const double one_hour_ms = 60.0 * 60.0 * MILLIUNITS;
double raw_time_ms = available_bytes / alloc_bytes_rate;
_predicted_time_until_next_gc_ms = MIN2(raw_time_ms, one_hour_ms);
}
// Estimate number of cards that need to be processed before next GC. There
// are no incoming cards when time is short, because in that case the
// controller activates refinement by mutator threads to stay on target even
// if threads deactivate in the meantime. This also covers the case of not
// having a real prediction of time until GC.
size_t incoming_cards = 0;
if (_predicted_time_until_next_gc_ms > _update_period_ms) {
double incoming_rate = analytics->predict_dirtied_cards_rate_ms();
double raw_cards = incoming_rate * _predicted_time_until_next_gc_ms;
incoming_cards = static_cast<size_t>(raw_cards);
}
size_t total_cards = num_cards + incoming_cards;
_predicted_cards_at_next_gc = total_cards;
// No concurrent refinement needed.
if (total_cards <= target_num_cards) {
// We don't expect to exceed the target before the next GC.
_threads_needed = 0;
return;
}
// The calculation of the number of threads needed isn't very stable when
// time is short, and can lead to starting up lots of threads for not much
// profit. If we're in the last update period, don't change the number of
// threads running, other than to treat the current thread as running. That
// might not be sufficient, but hopefully we were already reasonably close.
// We won't accumulate more because mutator refinement will be activated.
if (_predicted_time_until_next_gc_ms <= _update_period_ms) {
_threads_needed = MAX2(active_threads, 1u);
return;
}
// Estimate the number of cards that need to be refined before the next GC
// to meet the goal.
size_t cards_needed = total_cards - target_num_cards;
// Estimate the rate at which a thread can refine cards. If we don't yet
// have an estimate then only request one running thread, since we do have
// excess cards to process. Just one thread might not be sufficient, but
// we don't have any idea how many we actually need. Eventually the
// prediction machinery will warm up and we'll be able to get estimates.
double refine_rate = analytics->predict_concurrent_refine_rate_ms();
if (refine_rate == 0.0) {
_threads_needed = 1;
return;
}
// Estimate the number of refinement threads we need to run in order to
// reach the goal in time.
double thread_capacity = refine_rate * _predicted_time_until_next_gc_ms;
double nthreads = cards_needed / thread_capacity;
// Decide how to round nthreads to an integral number of threads. Always
// rounding up is contrary to delaying refinement work. But when we're
// close to the next GC we want to drive toward the target, so round up
// then. The rest of the time we round to nearest, trying to remain near
// the middle of the range.
if (_predicted_time_until_next_gc_ms <= _update_period_ms * 5.0) {
nthreads = ::ceil(nthreads);
} else {
nthreads = ::round(nthreads);
}
_threads_needed = static_cast<uint>(MIN2<size_t>(nthreads, UINT_MAX));
}