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

Change TaskRunner to limit context switches. #5532

Merged
merged 1 commit into from
Oct 6, 2019

Conversation

swankjesse
Copy link
Collaborator

Now we don't have to alternate between the coordinator thread and the task
thread between task runs if the task returns 0. Instead the task thread can
stay resident.

This implementation works by having task runnables that can switch from
the coordinator role (sleeping until the next task starts) and the executor
role.

#5512

@@ -56,8 +56,6 @@ abstract class Task(
/** Undefined unless this is in [TaskQueue.futureTasks]. */
internal var nextExecuteNanoTime = -1L

internal var runRunnable: Runnable? = null

/** Returns the delay in nanoseconds until the next execution, or -1L to not reschedule. */
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: Negative values < -1 seem to be effectively a priority, is that ok? Or should be an error?

Not a big concern given this is internal.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Intended contract is for implementations to never return such values! If ever we wanted to make this more broadly usable we’d need to enforce that requirement.

while (true) {
val task = synchronized(this@TaskRunner) {
awaitTaskToRun()
} ?: return
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think my biggest concern is that the delay of the time to execute a solitary task becomes the limiting time to execute a later (but soon) task on another queue.

Or am I missing something?

Do you schedule a kick for next next task time somewhere?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, awaitTaskToRun might be misnamed. It’ll always schedule a thread if there’s something to do now (run or sleep) that competes with something to do later (run or sleep).

var readyTask: Task? = null
var multipleReadyTasks = false

// Decide what to run. This loop's goal wants to:
Copy link
Collaborator

@yschimke yschimke Oct 5, 2019

Choose a reason for hiding this comment

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

Crazy Idea: Instead of this loop goal, could you store the next two scheduled times, and loop to find the third at this point?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Maybe the question I should answer is, why two items?

The first one is what we’re going to execute on the current thread.
The second one justifies starting a new thread that’ll itself look for up to two items.

We stop after two because once we know we’re going to fork a thread, it becomes that thread’s problem to fork a thread for the third thing.

@yschimke
Copy link
Collaborator

yschimke commented Oct 5, 2019

Overall, I'd love to see real world thread usage for this. Any thoughts of how it looks in a profiler? Or whether we could output something like the Google Chrome Trace Event format?

Copy link
Collaborator

@yschimke yschimke left a comment

Choose a reason for hiding this comment

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

Hard for me to conclusively review in-situ. Needs more eyes or enough bake time and testing before release.

@swankjesse
Copy link
Collaborator Author

Before & after effects of fewer context switches. These are measurements for the cost of 100 sequential empty tasks on the same queue.

BEFORE

Benchmark                                          (executionCount)    Mode    Cnt     Score    Error  Units
SelectBenchmark.executeTasks                                    100  sample  10730   930.829 ± 10.726  us/op
SelectBenchmark.executeTasks:executeTasks·p0.00                 100  sample          655.360           us/op
SelectBenchmark.executeTasks:executeTasks·p0.50                 100  sample          761.856           us/op
SelectBenchmark.executeTasks:executeTasks·p0.90                 100  sample         1476.608           us/op
SelectBenchmark.executeTasks:executeTasks·p0.95                 100  sample         1673.216           us/op
SelectBenchmark.executeTasks:executeTasks·p0.99                 100  sample         2012.549           us/op
SelectBenchmark.executeTasks:executeTasks·p0.999                100  sample         3156.435           us/op
SelectBenchmark.executeTasks:executeTasks·p0.9999               100  sample         3675.513           us/op
SelectBenchmark.executeTasks:executeTasks·p1.00                 100  sample         3678.208           us/op

AFTER

Benchmark                                          (executionCount)    Mode    Cnt     Score   Error  Units
SelectBenchmark.executeTasks                                    100  sample  24266   412.118 ± 1.106  us/op
SelectBenchmark.executeTasks:executeTasks·p0.00                 100  sample          375.808          us/op
SelectBenchmark.executeTasks:executeTasks·p0.50                 100  sample          394.752          us/op
SelectBenchmark.executeTasks:executeTasks·p0.90                 100  sample          445.952          us/op
SelectBenchmark.executeTasks:executeTasks·p0.95                 100  sample          488.960          us/op
SelectBenchmark.executeTasks:executeTasks·p0.99                 100  sample          635.218          us/op
SelectBenchmark.executeTasks:executeTasks·p0.999                100  sample          959.896          us/op
SelectBenchmark.executeTasks:executeTasks·p0.9999               100  sample         1423.714          us/op
SelectBenchmark.executeTasks:executeTasks·p1.00                 100  sample         1501.184          us/op

@swankjesse
Copy link
Collaborator Author

Figuring out how to mature this up is the next challenge. The best way to do that is some real world exercise. I’m going to run our crawler a bunch, and maybe write some torture tests.

@swankjesse swankjesse force-pushed the jwilson.1004.new_task_runner branch from b785000 to 264e932 Compare October 6, 2019 01:59
Now we don't have to alternate between the coordinator thread and the task
thread between task runs if the task returns 0. Instead the task thread can
stay resident.

This implementation works by having task runnables that can switch from
the coordinator role (sleeping until the next task starts) and the executor
role.

#5512
@swankjesse swankjesse force-pushed the jwilson.1004.new_task_runner branch from 264e932 to ef4b5ec Compare October 6, 2019 03:14
@@ -87,7 +81,7 @@ class TaskQueue internal constructor(
fun awaitIdle(delayNanos: Long): Boolean {
val latch = CountDownLatch(1)

val task = object : Task("awaitIdle") {
val task = object : Task("awaitIdle", cancelable = false) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this was the problem with flaky tests!

@@ -134,6 +134,9 @@ class CallKotlinTest {
recordedRequest = server.takeRequest()
assertEquals("HEAD", recordedRequest.method)

recordedRequest = server.takeRequest()
assertThat(recordedRequest.failure).isNotNull()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this fixes a green/green collision with two earlier pull requests

@swankjesse swankjesse merged commit afd9db3 into master Oct 6, 2019
@swankjesse swankjesse mentioned this pull request Oct 6, 2019
15 tasks
@swankjesse swankjesse deleted the jwilson.1004.new_task_runner branch January 1, 2020 19:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants