Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call System.gc between work requests #558

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions src/main/kotlin/io/bazel/worker/CpuTimeBasedGcScheduler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.bazel.worker

import com.sun.management.OperatingSystemMXBean
import java.lang.management.ManagementFactory
import java.time.Duration
import java.util.concurrent.atomic.AtomicReference

// This class is intended to mirror https://github.com/Bencodes/bazel/blob/3835d9b21ad524d06873dfbf465ffd2dfb635ba8/src/main/java/com/google/devtools/build/lib/worker/WorkRequestHandler.java#L431-L474
class CpuTimeBasedGcScheduler(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It maybe worthwhile for future readers to inline the link to bazel source and part of the PR description in a class comment :)

/**
* After this much CPU time has elapsed, we may force a GC run. Set to [Duration.ZERO] to
* disable.
*/
private val cpuUsageBeforeGc: Duration
) {

/** The total process CPU time at the last GC run (or from the start of the worker). */
private val cpuTime: Duration
get() = if (cpuUsageBeforeGc.isZero) Duration.ZERO else Duration.ofNanos(bean.processCpuTime)
private val cpuTimeAtLastGc: AtomicReference<Duration> = AtomicReference(cpuTime)

/** Call occasionally to perform a GC if enough CPU time has been used. */
fun maybePerformGc() {
if (!cpuUsageBeforeGc.isZero) {
val currentCpuTime = cpuTime
val lastCpuTime = cpuTimeAtLastGc.get()
// Do GC when enough CPU time has been used, but only if nobody else beat us to it.
if (currentCpuTime.minus(lastCpuTime).compareTo(cpuUsageBeforeGc) > 0
&& cpuTimeAtLastGc.compareAndSet(lastCpuTime, currentCpuTime)
) {
System.gc()
// Avoid counting GC CPU time against CPU time before next GC.
cpuTimeAtLastGc.compareAndSet(currentCpuTime, cpuTime)
}
}
}

companion object {
/** Used to get the CPU time used by this process. */
private val bean = ManagementFactory.getOperatingSystemMXBean() as OperatingSystemMXBean
}
}
9 changes: 6 additions & 3 deletions src/main/kotlin/io/bazel/worker/PersistentWorker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.PrintStream
import java.nio.charset.StandardCharsets.UTF_8
import java.time.Duration
import java.util.concurrent.Executors
import kotlin.coroutines.CoroutineContext

Expand All @@ -55,11 +56,11 @@ import kotlin.coroutines.CoroutineContext
*/
class PersistentWorker(
private val coroutineContext: CoroutineContext,
private val captureIO: () -> IO
private val captureIO: () -> IO,
private val cpuTimeBasedGcScheduler: CpuTimeBasedGcScheduler,
) : Worker {

constructor() : this(Dispatchers.IO, IO.Companion::capture)

constructor() : this(Dispatchers.IO, IO.Companion::capture, CpuTimeBasedGcScheduler(Duration.ofSeconds(10)))

@ExperimentalCoroutinesApi
override fun start(execute: Work) = WorkerContext.run {
Expand All @@ -73,6 +74,8 @@ class PersistentWorker(
.forEach { request ->
launch {
compileWork(request, io, writeChannel, execute)
//Be a friendly worker by performing a GC between compilation requests
cpuTimeBasedGcScheduler.maybePerformGc()
}
}
}.invokeOnCompletion { writeChannel.close() }
Expand Down