/*
 * Copyright (c) 2022, 2025, 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 "gc/g1/g1Analytics.hpp"
#include "gc/g1/g1ConcurrentRefineThreadsNeeded.hpp"
#include "gc/g1/g1HeapRegion.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. 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) {
  _predicted_time_until_next_gc_ms = _policy->predict_time_to_next_gc_ms(available_bytes);

  // Estimate number of cards that need to be processed before next GC.
  const G1Analytics* analytics = _policy->analytics();

  double incoming_rate = analytics->predict_dirtied_cards_rate_ms();
  double raw_cards = incoming_rate * _predicted_time_until_next_gc_ms;
  size_t 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 needed.  That might not be sufficient, but hopefully we were
  // already reasonably close.
  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.
  double rthreads = nthreads;
  if (_predicted_time_until_next_gc_ms <= _update_period_ms * 5.0) {
    rthreads = ::ceil(nthreads);
  } else {
    rthreads = ::round(nthreads);
  }

  _threads_needed = static_cast<uint>(MIN2<size_t>(rthreads, UINT_MAX));
}
