Как уменьшить джиттер для Java? - PullRequest
22 голосов
/ 15 ноября 2011

Для решения этой проблемы я создал открытый исходный код Java Thread Affinity library

Когда несколько потоков тесно взаимодействуют, это может уменьшить задержку и увеличить пропускную способность. Для однопоточных задач он все еще может немного уменьшить дрожание.


Эта программа просматривает разницу во времени между вызовами на System.nanoTime() и сообщает о них более 10x, 000 нс.

public class TimeJumpingMain {
    static final long IGNORE_TIME = 1000 * 1000 * 1000; // the first second to allow warmup.
    static final int minJump = 10; // smallest jump of 10 us.
    static final int midJump = 100; // mid size jump of 100 us.
    static final int bigJump = 1000; // big jump of 1 ms.

    public static void main(String... args) {
        int[] intervalTimings = new int[1000];
        int[] jumpTimings = new int[1000];

        long start = System.nanoTime();
        long prev = start;
        long prevJump = start;
        int jumpCount = 0;
        int midJumpCount = 0;
        int bigJumpCount = 0;

        while (true) {
            long now = System.nanoTime();
            long jump = (now - prev) / 1000;
            if (jump > minJump && now - start > IGNORE_TIME) {
                long interval = (now - prevJump) / 1000;
                if (jumpCount < intervalTimings.length) {
                    intervalTimings[jumpCount] = (int) interval;
                    jumpTimings[jumpCount] = (int) jump;
                }
                if (jump >= midJump)
                    midJumpCount++;
                if (jump >= bigJump)
                    bigJumpCount++;
                prevJump = now;
                jumpCount++;
            }
            prev = now;
            if (now - start > 120L * 1000 * 1000 * 1000 + IGNORE_TIME)
                break;
        }
        System.out.println("interval us\tdelay us");
        for (int i = 0; i < jumpCount && i < intervalTimings.length; i++) {
            System.out.println(intervalTimings[i] + "\t" + jumpTimings[i]);
        }
        System.out.printf("Time jumped %,d / %,d / %,d times by at least %,d / %,d / %,d us in %.1f seconds %n",
                jumpCount, midJumpCount, bigJumpCount, minJump, midJump, bigJump, (System.nanoTime() - start - IGNORE_TIME) / 1e9);
    }
}

на моей машине это сообщает

Time jumped 2,905 / 131 / 20 times by at least 10 / 100 / 1,000 us in 120.0 seconds   

Я попытался chrt установить приоритет в реальном времени и taskset, чтобы попытаться подключиться к одному ядру после запуска процесса, но это не помогло, как я ожидал.

Я настроил коробку для перемещения всех прерываний на процессор 0-3 и маску процессора для всего процесса с 0xFF до 0x0F. В top первые четыре процессора простаивают на ~ 99%, а последние четыре процессора простаивают на 100,0%.

Использование chrt -r 99 в качестве root

Time jumped 673 / 378 / 44 times by at least 10 / 100 / 1,000 us in 120.0 seconds 

Однако при использовании только taskset -c 7 (я убедился, что cpu7 свободен)

Time jumped 24 / 1 / 0 times by at least 10 / 100 / 1,000 us in 120.0 seconds 

Использование chrt - r 99 taskset -c 7

Time jumped 7 / 1 / 0 times by at least 10 / 100 / 1,000 us in 120.0 seconds  

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

Более широкий вопрос:

Как уменьшить джиттер для процесса Java? Есть ли еще советы по снижению джиттера в Linux?

ПРИМЕЧАНИЕ: GC не происходит во время выполнения этого процесса (проверено с -verbosegc)

Похоже, что компиляция кода может вызывать задержку 3,62 мс каждый раз после 100 - 102 мс. По этой причине я игнорирую все в первую секунду как разогрев.

1 Ответ

16 голосов
/ 16 ноября 2011

Есть системный джиттер и JVM джиттер.

Для первого вы можете использовать параметр isolcpus во время загрузки, чтобы гарантировать, что на этих процессорах может работать только код вашего приложения

http://www.novell.com/support/viewContent.do?externalId=7009596&sliceId=1

В идеалевыполните jni-вызов (в свою собственную jni-библиотеку) до sched_setaffinity только для активного потока, чтобы у вас действительно не было ничего, кроме этого потока, работающего там.

По моему опыту, дрожание системы минимизируется использованиемИзоляция с прерываниями, обрабатываемыми только определенными ядрами, отключение гиперпоточности и полное исключение использования управления питанием (это параметры BIOS, когда они доступны для отключения всего управления c-состояниями и p-состояниями) во время работы вашего приложения.на экранированных сердечниках.Специфичные для bios опции, очевидно, специфичны для вашей материнской платы, поэтому вам нужно изучить ее в зависимости от модели вашей материнской платы.

Еще один аспект, который нужно рассмотреть на системном уровне, - это локальное прерывание APIC (LOC, локальный счетчик прерываний) частота.Это «рабочий стол с низкой задержкой», использующий прерывания 1 кГц?в любом случае, вы можете ожидать, что джиттер будет сгруппирован вокруг интервала прерывания

2 больше, о котором я практически ничего не знаю, но осознаю как источники джиттера;Ядро tlb сбрасывает прерывания и пользовательское пространство tlb сбрасывает прерывания.Некоторые ядра RT предлагают опции для управления ими, так что это может быть другой вещью, которую нужно рассмотреть.Вы также можете посмотреть на этот сайт о создании приложений RT на ядре RT для получения дополнительных советов.

...