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

Use thread pool for test runner #4614

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

HollandDM
Copy link

@HollandDM HollandDM commented Feb 23, 2025

This pull request (PR) aims to address issue #4590.
It updates the test runner and test module to allow the runner to spawn new threads during execution, while respecting the total number of threads Mill can utilize (as defined by the --jobs flag).

To prevent the test runner from overwhelming the system with threads, the parent test module must grant permissions to its child runners. A child runner can only spawn a new thread with this permission, and it will "return" the thread to the parent module upon completion or process termination. Inter-process communication is currently implemented using a memory-mapped file for simplicity and performance.

Note that this PR does not yet implement a work-stealing thread pool. The primary goal is to verify the feasibility of this approach and identify any necessary adjustments. If this direction proves suitable, I'll follow up with the work-stealing thread pool implementation, which should be straightforward given the existing foundation.

@HollandDM HollandDM force-pushed the work-stealing-test-runner branch from c09aa01 to 20eccb3 Compare February 23, 2025 16:10
@HollandDM
Copy link
Author

Local run on my machine yield similar test time when run scalalib.test.testForked with grouping on and off. But do I need some kind of benchmark for this?

@lihaoyi
Copy link
Member

lihaoyi commented Feb 24, 2025

Let's go with the multiple-JVMs-one-thread-each architecture described in the original ticket, rather than the one-JVM-multiple-threads architecture you have in this PR.

Having each JVM be single-threaded running tests makes things like resource usage much more attributable and deterministic, since the resource footprint of each JVM can only be due to the one test it is currently running. Whereas if you put a bunch of tests into a single JVM and it crashes due to OOM, you have no idea which test is at fault.

In terms of performance, I think the goal should be:

  1. Running heavyweight test classes like scalalib.test.testForked, it should have no significant performance difference with testForkGrouping enabled or disabled

  2. Running lightweight test classes, say 1000 lightweight test classes that do nothing, it should be much more performant than having testForkGrouping configured to one-test-per-class, and comparable to testForkGrouping disabled (although maybe not quite the same, depending on whether the overhead of N different JVMs or whether the speedup from running N times parallel wins out)

@HollandDM
Copy link
Author

Hmm, look like i misunderstood the ideal of the original ticket. From what you said, it seems like each test groups can now spawn multiple sub processes (with respect to total --jobs config), and they can perform work stealing between them, right?
Also, for the performance goal, number 1 is easy to setup. But for number 2, do we have existing test like this, or should I create new one, and add it in this PR also?

@lihaoyi
Copy link
Member

lihaoyi commented Feb 24, 2025

@HollandDM that's right.

In terms of the (2) benchmarks, feel free to include it in this PR as an integration test. You can have the test code generate the necessary project files before running the test to avoid having to commit all the boilerplate code

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.

2 participants