Gradle: оптимизация параллельных тестов

я экспериментирую с возможностью Gradle для запуска тестов параллельно. Главный параметр я нашел это maxParallelForks собственность тест задач. Я ожидал, что поведение этой настройки будет похоже на наличие Executors.newFixedThreadPool для выполнения тестов. А именно, фиксированное количество потоков (процессов в случае Gradle) выполняется одновременно; всякий раз, когда один поток завершает работу, новый активируется в пуле.

, в поведение Gradle принципиально отличается менее оптимальным образом. Похоже, что Gradle делит тестовые классы на число, равное maxParallelForks групп, а затем Gradle порождает процесс для каждой группы и позволяет этим процессам выполняться параллельно. Проблема с этой стратегией очевидна: она не может динамически корректировать выполнение на основе времени, необходимого тестовому классу.

например, предположим, что у вас есть 5 классов и maxParallelForks установлено значение 2. Среди пяти классы, есть медленный, а остальные относительно быстрые. Идеальной стратегией было бы позволить одному процессу выполнить медленный, а другому-быстрый. Однако Gradle группирует медленный вместе с одним или двумя быстрыми и порождает два процесса для выполнения двух групп классов, что, безусловно, менее оптимально, чем идеальный случай.

вот простая демонстрация.

медленный класс:

class DemoTest {
    @Test
    void one() {
        Thread.sleep( 5000 )
        println System.getProperty('org.gradle.test.worker') + ": " + new Date().format('HH:mm:ss')
        assert 1 == 1
    }

    @Test
    void two() {
        Thread.sleep( 5000 )
        println System.getProperty('org.gradle.test.worker') + ": " + new Date().format('HH:mm:ss')
        assert 1 == 1
    }
}

быстрые классы (DemoTest2-4, с идентичное тело класса):

class DemoTest2 {
    @Test
    void one() {
        Thread.sleep( 1000 )
        println System.getProperty('org.gradle.test.worker') + ": " + new Date().format('HH:mm:ss')
        assert 1 == 1
    }

    @Test
    void two() {
        Thread.sleep( 1000 )
        println System.getProperty('org.gradle.test.worker') + ": " + new Date().format('HH:mm:ss')
        assert 1 == 1
    }
}

все классы находятся в пакете junit, который, оказывается, то же самое имя, что и знаменитый тестовый фреймворк : -)

вот возможный выход:

junit.DemoTest2 > one STANDARD_OUT
    2: 14:54:00

junit.DemoTest2 > two STANDARD_OUT
    2: 14:54:01

junit.DemoTest4 > one STANDARD_OUT
    2: 14:54:02

junit.DemoTest4 > two STANDARD_OUT
    2: 14:54:03

junit.DemoTest > one STANDARD_OUT
    3: 14:54:04

junit.DemoTest > two STANDARD_OUT
    3: 14:54:09

junit.DemoTest3 > one STANDARD_OUT
    3: 14:54:10

junit.DemoTest3 > two STANDARD_OUT
    3: 14:54:11

junit.DemoTest5 > one STANDARD_OUT
    3: 14:54:12

junit.DemoTest5 > two STANDARD_OUT
    3: 14:54:13

как вы можете видеть, медленные класс DemoTest группируется с двумя быстрыми классами. Общее время выполнения составляет около 13 секунд, что могло бы составлять 10 секунд, если бы быстрые классы были сгруппированы вместе.

Итак, есть ли простой способ, чтобы оптимизировать этот поведение в Gradle без обращения к пользовательскому JUnit runner?

спасибо.

1 ответов


Это может быть оптимизировано только путем внесения изменений в кодовую базу Gradle.