-
-
Notifications
You must be signed in to change notification settings - Fork 331
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
Allow grouping and parallelization of test targets within a single module #3478
Draft
lihaoyi
wants to merge
23
commits into
com-lihaoyi:main
Choose a base branch
from
lihaoyi:test-par
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
lihaoyi
changed the title
Parallelize test targets on a per-test-class basis
Allow grouping and parallelization of test targets within a single module
Sep 17, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This PR allows Mill to parallelize test suites on a per-test-class basis. This is roughly a port of the SBT
testGrouping
feature https://www.scala-sbt.org/1.x/docs/Testing.html#Forking+tests and serves the same purpose. This is most useful for codebases with large modules each of which has a lot of test classes within them, and the flexible nature ofdef testForkGrouping
gives the user room to tune exactly how the tests are grouped for maximal performance:For example, running
time ./mill -i scalalib.test
with and without test grouping on my 10 core macbook pro (after breaking upHelloWorldTests.scala
for better granularity), we see about a 3x speedup.Without Test Grouping, all test classes in 1 JVM (default)
With Test Grouping, 3 test classes per JVM (
def testForkGrouping = discoveredTestClasses().grouped(3).toSeq
)With Test Grouping, 1 test class per JVM (
def testForkGrouping = discoveredTestClasses().grouped(1).toSeq
)The limited speedup is likely due to the heavy nature of Mill tests meaning that running sequentially they already use multiple cores, and I would expect a greater speedup for most projects whose tests would be more lightweight. We can also see that 1-test-class-per-JVM is somewhat slower than 3-test-classes-per-JVM in this case, likely due to JVM overhead becoming significant
This feature is opt-in via
def testForkGrouping = discoveredTestClasses().map(Seq(_))
. The default behavior of running all tests in a single JVM is the unchangedImplementation Notes
We re-use the same
ExecutionContext
that Mill uses internally for scheduling its targets, allowing the scheduling to be cooperative.We convert the default
FixedThreadPool
into aForkJoinPool
provide ablocking{...}
operation to allow theForkJoinPool
to spawn an additional thread when an existing thread is blocked waiting.Future
s spawned for each test class, as the task-level thread is idle and we want to continue making use of the available CPUs.scala.concurrent.ExecutionContext.global
does, butglobal
s implementation is private and not re-usable link and so I have to duplicate the small amount of code wiring it upEach test class runs in a subprocess in a separate JVM with a separate sandbox folder, and their outputs are then all read and consolidated back into the combined output for the original test task.
I added a flag
testParallelizeClasses
that can be set offalse
to fall back to the existing behavior