[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
@@ -320,10 +320,6 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
null
}
})
if (memoryModel != MemoryModel.EXPERIMENTAL && arguments.gcAggressive) {
configuration.report(ERROR, "-Xgc-aggressive is only supported for -memory-model experimental")
}
put(GARBAGE_COLLECTOR_AGRESSIVE, arguments.gcAggressive)
put(PROPERTY_LAZY_INITIALIZATION, when (arguments.propertyLazyInitialization) {
null -> {
when (memoryModel) {
@@ -330,9 +330,6 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@Argument(value="-Xgc", valueDescription = "<gc>", description = "GC to use, 'noop' and 'stms' are currently supported. Works only with -memory-model experimental")
var gc: String? = null
@Argument(value="-Xgc-aggressive", description = "Make GC agressive. Works only with -memory-model experimental")
var gcAggressive: Boolean = false
@Argument(value = "-Xir-property-lazy-initialization", valueDescription = "{disable|enable}", description = "Initialize top level properties lazily per file")
var propertyLazyInitialization: String? = null
@@ -5,8 +5,10 @@
package org.jetbrains.kotlin.backend.konan
// Must match `GCSchedulerType` in CompilerConstants.hpp
enum class GCSchedulerType(val value: Int) {
DISABLED(0),
WITH_TIMER(1),
ON_SAFE_POINTS(2)
}
ON_SAFE_POINTS(2),
AGGRESSIVE(3),
}
@@ -94,7 +94,6 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
}
realGc
}
val gcAggressive: Boolean get() = configuration.get(KonanConfigKeys.GARBAGE_COLLECTOR_AGRESSIVE)!!
val runtimeAssertsMode: RuntimeAssertsMode get() = configuration.get(BinaryOptions.runtimeAssertionsMode) ?: RuntimeAssertsMode.IGNORE
val workerExceptionHandling: WorkerExceptionHandling get() = configuration.get(KonanConfigKeys.WORKER_EXCEPTION_HANDLING)!!
val runtimeLogs: String? get() = configuration.get(KonanConfigKeys.RUNTIME_LOGS)
@@ -119,7 +118,6 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
val gcSchedulerType: GCSchedulerType by lazy {
configuration.get(BinaryOptions.gcSchedulerType) ?: when {
!target.supportsThreads() -> GCSchedulerType.ON_SAFE_POINTS
gcAggressive -> GCSchedulerType.ON_SAFE_POINTS
else -> GCSchedulerType.WITH_TIMER
}
}
@@ -161,7 +161,6 @@ class KonanConfigKeys {
val DESTROY_RUNTIME_MODE: CompilerConfigurationKey<DestroyRuntimeMode>
= CompilerConfigurationKey.create("when to destroy runtime")
val GARBAGE_COLLECTOR: CompilerConfigurationKey<GC> = CompilerConfigurationKey.create("gc")
val GARBAGE_COLLECTOR_AGRESSIVE: CompilerConfigurationKey<Boolean> = CompilerConfigurationKey.create("turn on agressive GC mode")
val CHECK_LLD_COMPATIBILITY: CompilerConfigurationKey<Boolean> = CompilerConfigurationKey.create("check compatibility with LLD")
val PROPERTY_LAZY_INITIALIZATION: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("lazy top level properties initialization")
@@ -2735,7 +2735,6 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
return
overrideRuntimeGlobal("Kotlin_destroyRuntimeMode", Int32(context.config.destroyRuntimeMode.value))
overrideRuntimeGlobal("Kotlin_gcAggressive", Int32(if (context.config.gcAggressive) 1 else 0))
overrideRuntimeGlobal("Kotlin_workerExceptionHandling", Int32(context.config.workerExceptionHandling.value))
overrideRuntimeGlobal("Kotlin_freezingEnabled", Int32(if (context.config.freezing.enableFreezeAtRuntime) 1 else 0))
overrideRuntimeGlobal("Kotlin_gcSchedulerType", Int32(context.config.gcSchedulerType.value))
@@ -113,7 +113,7 @@ ext.isNoopGC = project.globalTestArgs.contains("-Xgc=noop")
if (ext.isExperimentalMM) {
// TODO: Make it more aggressive and only turn it on for a subset of tests.
tasks.withType(KonanCompileNativeBinary.class).configureEach {
extraOpts "-Xgc-aggressive"
extraOpts "-Xbinary=gcSchedulerType=aggressive"
}
}
@@ -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;
}