[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:
committed by
Space
parent
56e64d78e3
commit
cc8f278948
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user