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

Limit max pool size for dynamic parallel execution #3206

Merged

Conversation

mpkorstanje
Copy link
Contributor

@mpkorstanje mpkorstanje commented Mar 24, 2023

Overview

This is a followup of #3044 for the dynamic strategy.

With Java 9+ the ForkJoinPool used by JUnit can be configured with a maximum pool size via the junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor property.

While the number of concurrently executing tests may exceed the configured parallelism factor when tests become blocked, it will not exceed the maximum pool size factor in these circumstances.

With the following configuration:

junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.config.strategy=dynamic
junit.jupiter.execution.parallel.mode.default=concurrent
junit.jupiter.execution.parallel.config.dynamic.factor=0.1667

This example will report between 2 and 4 tests running concurrently on a system with 12 available cores while only (12 * 0.1667 =) 2 would be expected.

class ExampleTest {

    private static final AtomicInteger running = new AtomicInteger();

    @BeforeEach
    void increment() {
        System.out.println("Running " + running.incrementAndGet());
    }

    @AfterEach
    void decrement() {
        running.decrementAndGet();
    }

    static IntStream numbers() {
        return IntStream.range(0, 1000);
    }

    @ParameterizedTest
    @MethodSource("numbers")
    void test(int i) throws ExecutionException, InterruptedException {
        Runnable sleep = () -> {
            try {
                Thread.sleep(600);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        };
        ForkJoinPool.commonPool().submit(sleep).get();
    }
}

By also configuring the max-pool-size-factor we can ensure the concurrently executing test does not exceed the expected 2 .

junit.jupiter.execution.parallel.config.dynamic.max-pool-size-factor=1

Additionally, because the ForkJoinPool will by default reject tasks that would exceed the maximum pool size the
junit.jupiter.execution.parallel.config.dynamic.saturate property has been added and will default to true. There appears to be no reason to ever set this false but it is there should someone depend on the old behavior.

Fixes: #3205


I hereby agree to the terms of the JUnit Contributor License Agreement.


Definition of Done

This is a followup of junit-team#3044 for the `dynamic` strategy.

Fixes: junit-team#3205
@mpkorstanje mpkorstanje force-pushed the saturate-via-property-dynamic branch from 64cd26b to de89325 Compare March 24, 2023 14:26
"Factor '%s' specified via configuration parameter '%s' must be greater than or equal to 1",
factor, CONFIG_DYNAMIC_FACTOR_PROPERTY_NAME));
return maxPoolSizeFactor.multiply(BigDecimal.valueOf(parallelism)).intValue();
}).orElseGet(() -> 256 + parallelism);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are two ways to calculate the max pool size:

maxPoolSize = dynamic.max-pool-size-factor * dynamic.factor * Runtime.getRuntime().availableProcessors() 
maxPoolSize = dynamic.max-pool-size-factor * Runtime.getRuntime().availableProcessors() 

And the choice is a bit arbitrary. But I think reasoning in terms of "threads per degree of parallelism" is a bit easier so I prefer the former.

@mpkorstanje mpkorstanje marked this pull request as ready for review March 24, 2023 14:48
@marcphilipp marcphilipp added this to the 5.10.0-M1 milestone Apr 22, 2023
@mpkorstanje mpkorstanje force-pushed the saturate-via-property-dynamic branch from fca3426 to 6849f00 Compare April 23, 2023 12:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Getting out of memory error while running test in parallel
3 participants