#include <LoadBalancerS2.hpp>
#include <primecount-internal.hpp>
#include <StatusS2.hpp>
#include <Sieve.hpp>
#include <imath.hpp>
#include <int128_t.hpp>
#include <min.hpp>
#include <stdint.h>
namespace primecount {
LoadBalancerS2::LoadBalancerS2(maxint_t x,
int64_t sieve_limit,
maxint_t sum_approx,
int threads,
bool is_print) :
sieve_limit_(sieve_limit),
sum_approx_(sum_approx),
time_(get_time()),
is_print_(is_print),
status_(x)
{
int64_t sieve_bytes = 128 << 10;
int64_t numbers_per_byte = 30;
int64_t sqrt_limit = isqrt(sieve_limit);
max_size_ = max(sieve_bytes * numbers_per_byte, sqrt_limit);
if (threads == 1 &&
!is_print)
{
segment_size_ = max_size_;
segments_ = 100;
}
else
{
segment_size_ = isqrt(isqrt(x));
segments_ = 1;
}
int64_t min_size = 1 << 9;
segment_size_ = max(min_size, segment_size_);
segment_size_ = Sieve::get_segment_size(segment_size_);
}
maxint_t LoadBalancerS2::get_sum() const
{
return sum_;
}
bool LoadBalancerS2::get_work(ThreadSettings& thread)
{
LockGuard lockGuard(lock_);
sum_ += thread.sum;
if (is_print_)
{
uint64_t dist = thread.segments * thread.segment_size;
uint64_t high = thread.low + dist;
status_.print(high, sieve_limit_, sum_, sum_approx_);
}
update_load_balancing(thread);
thread.low = low_;
thread.segments = segments_;
thread.segment_size = segment_size_;
thread.sum = 0;
thread.secs = 0;
thread.init_secs = 0;
low_ += segments_ * segment_size_;
bool is_work = thread.low < sieve_limit_;
return is_work;
}
void LoadBalancerS2::update_load_balancing(const ThreadSettings& thread)
{
if (thread.low > max_low_)
{
max_low_ = thread.low;
segments_ = thread.segments;
if (sum_ == 0)
return;
if (segment_size_ < max_size_)
update_segment_size();
else
update_number_of_segments(thread);
}
}
void LoadBalancerS2::update_segment_size()
{
segment_size_ += segment_size_ / 16;
segment_size_ = min(segment_size_, max_size_);
segment_size_ = Sieve::get_segment_size(segment_size_);
}
void LoadBalancerS2::update_number_of_segments(const ThreadSettings& thread)
{
double rem_secs = remaining_secs() / 3;
double min_secs = 0.001;
double divider = max(min_secs, thread.secs);
double factor = rem_secs / divider;
double init_secs = max(min_secs, thread.init_secs);
double init_factor = in_between(50, (3600 * 6) / init_secs, 5000);
if (thread.secs > min_secs &&
thread.secs > thread.init_secs * init_factor)
{
double old = factor;
double next_runtime = thread.init_secs * init_factor;
factor = next_runtime / thread.secs;
factor = min(factor, old);
}
if (thread.secs > 0 &&
thread.secs * factor < thread.init_secs * 20)
{
double next_runtime = thread.init_secs * 20;
double current_runtime = thread.secs;
factor = next_runtime / current_runtime;
}
factor = in_between(0.5, factor, 2.0);
double next_runtime = thread.secs * factor;
if (next_runtime < min_secs)
segments_ *= 2;
else
{
double new_segments = std::round(segments_ * factor);
segments_ = (int64_t) new_segments;
segments_ = max(segments_, 1);
}
}
double LoadBalancerS2::remaining_secs() const
{
double percent = status_.getPercent(low_, sieve_limit_, sum_, sum_approx_);
percent = in_between(10, percent, 100);
double total_secs = get_time() - time_;
double secs = total_secs * (100 / percent) - total_secs;
return secs;
}
}