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

Support for coordinating parallelism across a multi-project gradle test execution? #3076

Open
danielstravito opened this issue Oct 26, 2022 · 3 comments

Comments

@danielstravito
Copy link

danielstravito commented Oct 26, 2022

Perhaps a feature request. The use case I have is that I have a large Kotlin project with over a hundred subprojects. Each subproject declares 1-30 test classes or so. To allow the CI and local development to run fast I want to saturate all cores at all times.

If I enable gradle's setting org.gradle.parallel=true it will spawn workers matching CPU core count (numCPU). This will result in for example 32 subproject's junit suites running at the same time. If the test task scheduler decides to put the subproject with the largest amount of test classes last, it will then utilize one single core at the end once the other parallel workers have finished off the rest of the subprojects. To workaround this, I could enable junit concurrency that would run test classes in parallel within each subproject. Problem with this is that I would overtax the CPU by having numCPU * numCPU concurrent test classes running at the same time.

So this brings me to the subject of this issue, is there any way of coordinating multiple gradle subproject junit executions in a way that at a maximum numCPU test classes are invoked across multiple workers (forked vm's) at any given time?

The gradle slack support channel suggested this would be a junit feature. With an ever growing number of cores in modern CPUs - even in laptops, this feature will be ever more relevant, no core deserves to be idle nor overtaxed.

@marcphilipp
Copy link
Member

One option would be to define an "aggregator" Gradle subproject that defines a Gradle Test task and contains a @Suite class that runs all tests. That subproject would then have dependencies on the test source sets of all other subprojects.

Another alternative would be to help Gradle to start the most costly tasks first. Instead of running ./gradlew build (or similar), you'd run ./gradlew :expensive-subproject-a:test :expensive-subproject-b:test build. Listing those tasks explicitly is redundant but would influence Gradle's task scheduler to start them as soon as possible.

@danielstravito
Copy link
Author

danielstravito commented Oct 31, 2022

Option 2 is bound to bitrot as performance characteristics and architecture changes over time.

Option 1 sounds interesting. Would such a @Suite class auto-discover all test classes that are in scope from the gradle dependencies? So it would mean one jvm fork and then junit schedules test classes across available cores? I guess it would require maintaining a dependencies { ... } section whenever a subproject is added/removed unless it's possible to gather such dependencies automatically? At least a less volatile category of information to maintain.

@nbrugger-tgm
Copy link

nbrugger-tgm commented Mar 21, 2023

This is probably a little too cutting edge, but Virtual Threads in Java 21 or 17 with --preview-enabled would solve the overtaxing. Since you can declare a custom thread spawner / "parallelizer" in JUnit, you could possibly implement it without work from JUnit side.

The other solution i see is disabling parallelism on Gradle level (for test tasks)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants