[K/N] Add a separate aggressive GC scheduler

^KT-48537

Merge-request: KT-MR-5253
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
Alexander Shabalin
2021-12-20 11:04:46 +00:00
committed by Space
parent 56e64d78e3
commit cc8f278948
11 changed files with 51 additions and 47 deletions
@@ -18,7 +18,7 @@ using namespace kotlin;
namespace {
class GCEmptySchedulerData : public gc::GCSchedulerData {
void OnSafePoint(gc::GCSchedulerThreadData& threadData) noexcept override {}
void UpdateFromThreadData(gc::GCSchedulerThreadData& threadData) noexcept override {}
void OnPerformFullGC() noexcept override {}
void UpdateAliveSetBytes(size_t bytes) noexcept override {}
};
@@ -31,7 +31,7 @@ public:
return std::chrono::microseconds(config_.regularGcIntervalUs);
}) {}
void OnSafePoint(gc::GCSchedulerThreadData& threadData) noexcept override {
void UpdateFromThreadData(gc::GCSchedulerThreadData& threadData) noexcept override {
size_t allocatedBytes = threadData.allocatedBytes();
if (allocatedBytes > config_.allocationThresholdBytes) {
RuntimeAssert(static_cast<bool>(scheduleGC_), "scheduleGC_ cannot be empty");
@@ -75,7 +75,7 @@ public:
timeOfLastGcNs_(currentTimeCallbackNs_()),
scheduleGC_(std::move(scheduleGC)) {}
void OnSafePoint(gc::GCSchedulerThreadData& threadData) noexcept override {
void UpdateFromThreadData(gc::GCSchedulerThreadData& threadData) noexcept override {
size_t allocatedBytes = threadData.allocatedBytes();
if (allocatedBytes > config_.allocationThresholdBytes ||
currentTimeCallbackNs_() - timeOfLastGcNs_ >= config_.cooldownThresholdNs) {
@@ -96,6 +96,24 @@ private:
std::function<void()> scheduleGC_;
};
class GCSchedulerDataAggressive : public gc::GCSchedulerData {
public:
GCSchedulerDataAggressive(gc::GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept :
scheduleGC_(std::move(scheduleGC)) {
RuntimeLogInfo({kTagGC}, "Initialize GC scheduler config in the aggressive mode");
// TODO: Make it even more aggressive and run on a subset of backend.native tests.
config.threshold = 1000;
config.allocationThresholdBytes = 10000;
}
void UpdateFromThreadData(gc::GCSchedulerThreadData& threadData) noexcept override { scheduleGC_(); }
void OnPerformFullGC() noexcept override {}
void UpdateAliveSetBytes(size_t bytes) noexcept override {}
private:
std::function<void()> scheduleGC_;
};
KStdUniquePtr<gc::GCSchedulerData> MakeEmptyGCSchedulerData() noexcept {
return ::make_unique<GCEmptySchedulerData>();
@@ -111,6 +129,10 @@ KStdUniquePtr<gc::GCSchedulerData> MakeGCSchedulerDataWithoutTimer(
return ::make_unique<GCSchedulerDataWithoutTimer>(config, std::move(scheduleGC), std::move(currentTimeCallbackNs));
}
KStdUniquePtr<gc::GCSchedulerData> MakeGCShedulerDataAggressive(gc::GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept {
return ::make_unique<GCSchedulerDataAggressive>(config, std::move(scheduleGC));
}
} // namespace
KStdUniquePtr<gc::GCSchedulerData> kotlin::gc::MakeGCSchedulerData(SchedulerType type, gc::GCSchedulerConfig& config, std::function<void()> scheduleGC) noexcept {
@@ -121,6 +143,8 @@ KStdUniquePtr<gc::GCSchedulerData> kotlin::gc::MakeGCSchedulerData(SchedulerType
return MakeGCSchedulerDataWithTimer(config, std::move(scheduleGC));
case SchedulerType::kOnSafepoints:
return MakeGCSchedulerDataWithoutTimer(config, std::move(scheduleGC), []() { return konan::getTimeNanos(); });
case SchedulerType::kAggressive:
return MakeGCShedulerDataAggressive(config, std::move(scheduleGC));
}
}
@@ -29,17 +29,6 @@ struct GCSchedulerConfig {
std::atomic<uint64_t> cooldownThresholdNs = 200 * 1000 * 1000; // 200 milliseconds by default.
std::atomic<bool> autoTune = false;
std::atomic<uint64_t> regularGcIntervalUs = 200 * 1000; // 200 milliseconds by default.
GCSchedulerConfig() noexcept {
if (compiler::gcAggressive()) {
// TODO: Make a separate GCSchedulerData for the aggressive mode and move this log there.
RuntimeLogInfo({kTagGC}, "Initialize GC scheduler config in the aggressive mode");
// TODO: Make it even more aggressive and run on a subset of backend.native tests.
threshold = 1000;
allocationThresholdBytes = 10000;
cooldownThresholdNs = 0;
}
}
};
class GCSchedulerThreadData;
@@ -49,7 +38,7 @@ public:
virtual ~GCSchedulerData() = default;
// Called by different mutator threads.
virtual void OnSafePoint(GCSchedulerThreadData& threadData) noexcept = 0;
virtual void UpdateFromThreadData(GCSchedulerThreadData& threadData) noexcept = 0;
// Always called by the GC thread.
virtual void OnPerformFullGC() noexcept = 0;
@@ -63,19 +52,25 @@ public:
static constexpr size_t kFunctionPrologueWeight = 1;
static constexpr size_t kLoopBodyWeight = 1;
explicit GCSchedulerThreadData(GCSchedulerConfig& config, std::function<void(GCSchedulerThreadData&)> onSafePoint) noexcept :
config_(config), onSafePoint_(std::move(onSafePoint)) {
explicit GCSchedulerThreadData(GCSchedulerConfig& config, std::function<void(GCSchedulerThreadData&)> slowPath) noexcept :
config_(config), slowPath_(std::move(slowPath)) {
ClearCountersAndUpdateThresholds();
}
// Should be called on encountering a safepoint.
void OnSafePointRegular(size_t weight) noexcept {
if (compiler::getGCSchedulerType() == compiler::GCSchedulerType::kOnSafepoints) {
safePointsCounter_ += weight;
if (safePointsCounter_ < safePointsCounterThreshold_) {
// TODO: This is a weird design. Consider replacing switch+virtual functions with pimpl+separate compilation.
switch (compiler::getGCSchedulerType()) {
case compiler::GCSchedulerType::kOnSafepoints:
case compiler::GCSchedulerType::kAggressive:
safePointsCounter_ += weight;
if (safePointsCounter_ < safePointsCounterThreshold_) {
return;
}
OnSafePointSlowPath();
return;
default:
return;
}
OnSafePointSlowPath();
}
}
@@ -97,7 +92,7 @@ public:
private:
void OnSafePointSlowPath() noexcept {
onSafePoint_(*this);
slowPath_(*this);
ClearCountersAndUpdateThresholds();
}
@@ -110,7 +105,7 @@ private:
}
GCSchedulerConfig& config_;
std::function<void(GCSchedulerThreadData&)> onSafePoint_;
std::function<void(GCSchedulerThreadData&)> slowPath_;
size_t allocatedBytes_ = 0;
size_t allocatedBytesThreshold_ = 0;
@@ -134,7 +129,7 @@ public:
void SetScheduleGC(std::function<void()> scheduleGC) noexcept;
GCSchedulerThreadData NewThreadData() noexcept {
return GCSchedulerThreadData(config_, [this](auto& threadData) { gcData_->OnSafePoint(threadData); });
return GCSchedulerThreadData(config_, [this](auto& threadData) { gcData_->UpdateFromThreadData(threadData); });
}
private:
@@ -12,7 +12,6 @@ using namespace kotlin;
// These are defined by overrideRuntimeGlobals in IrToBitcode.kt
RUNTIME_WEAK int32_t Kotlin_destroyRuntimeMode = 1;
RUNTIME_WEAK int32_t Kotlin_gcAggressive = 0;
RUNTIME_WEAK int32_t Kotlin_gcSchedulerType = 2;
RUNTIME_WEAK int32_t Kotlin_workerExceptionHandling = 0;
RUNTIME_WEAK int32_t Kotlin_freezingEnabled = 1;
@@ -25,10 +24,6 @@ ALWAYS_INLINE compiler::DestroyRuntimeMode compiler::destroyRuntimeMode() noexce
return static_cast<compiler::DestroyRuntimeMode>(Kotlin_destroyRuntimeMode);
}
ALWAYS_INLINE bool compiler::gcAggressive() noexcept {
return Kotlin_gcAggressive != 0;
}
ALWAYS_INLINE compiler::WorkerExceptionHandling compiler::workerExceptionHandling() noexcept {
return static_cast<compiler::WorkerExceptionHandling>(Kotlin_workerExceptionHandling);
}
@@ -58,13 +58,12 @@ enum class WorkerExceptionHandling : int32_t {
enum class GCSchedulerType {
kDisabled = 0,
kWithTimer = 1,
kOnSafepoints = 2
kOnSafepoints = 2,
kAggressive = 3,
};
DestroyRuntimeMode destroyRuntimeMode() noexcept;
bool gcAggressive() noexcept;
ALWAYS_INLINE inline bool shouldContainDebugInfo() noexcept {
return KonanNeedDebugInfo != 0;
}