Санкт-Петербургская группа тестирования JVM


« Тайна случайных... | Main | MySQL »
20071211 вторник Декабрь 11, 2007

Нить, которую разбудит HotSpot

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

Что должен сделать HotSpot в такой ситуации? —

Очевидно, что первый вариант хорош для блокировок типа читатели/писатели (read/write или shared/exclusive)—там где вы создаете, скажем, запись в кэше в режиме exclusive и потом уведомляете всех, что запись готова и можно ее читать. Но в Java блокировки взаимоисключающие. Если мы разбудим все нити, победа достанется одной, а все другие тронутся и тут же заглохнут на блокировке. Эта ситуация называется futile wakeup (см. раздел 3.3 в этой статье).

Получается, что надо выбрать одного. И снова выбор: кого?

Если бы разработчики планировщиков были благородными людьми, то первый вариант был единственно возможным. Но зачем же тогда потребовался второй вариант?

А нужен он, чтобы кэш процессора "не остыл". Это значит, что скорее всего кэши данных, команд и TLB процессора, на котором выполнялась нить содержат данные этой нити и выбор ее позволит избежать перезагрузки кэшей. Это скажется на быстродействии самой нити, трафике межпроцессорных шин и шины памяти. Планировщик пытается найти здоровый баланс между распределением времени между всеми нитями и оптимальностью использования кэшей. Если же нить выполнялась достаточно давно, то можно не заботится о кэшах и помещать нить на наиболее свободный процессор.

Однако, HotSpot знает в этой ситуации больше, чем планировщик. Как правило, блокировками защищаются наборы данных и можно быть уверенным в том, что процесс, перехватывающий блокировку у другого, будет работать с теми же областями памяти, что и его предшественник. Таким образом, планировщику можно дать "подсказку" о том, на каком процессоре лучше разбудить нить. Исключается не только перезагрузка кэшей, но и "переход" модифицированных строк кэшей к другому процессору. Таким образом, Магомет, как процесс идет к горе данных, а не наоборот. Но и здесь не стоит забывать о разумном балансе нитей в очередях к процессорам.

Этот метод может особенно улучшать быстродействие на NUMA-архитектурах и совершенно не менять быстродействие на процессорах типа Niagara, имеющих общий L2$1 для всех ядер.

В данный момент реализация данной подсказки в HotSpot только планируется.

Надо сказать, что в HotSpot реализовано несколько дисциплин выбора следующей нити. Переключать их можно следующей опцией:

$ java -XX:SyncKnobs=QMode=x …

Где x—это номер дисциплины. "Честные" дисциплины—1 и 3. Их реализацию можно посмотреть в исходниках.

А убедиться, что это работает на практике можно с помощью следующей программы:

import java.util.concurrent.CyclicBarrier;

public class qMode {

    public static final int N = 5;

    private static class T extends Thread {
        private int _n;
        private CyclicBarrier _barrier;

        public T(int n, CyclicBarrier b) {
            _n = n;
            _barrier = b;
        }

        public void run() {
            try {
                _barrier.await();
                Thread.sleep(_n * 10);
                while ( true ) {
                    synchronized ( T.class ) {
                        System.out.print(_n);
                        Thread.sleep(100);
                    }
                }
            } catch ( Exception e ) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(N + 1);
        for ( int i = 0; < N; i++ ) {
            new T(i, barrier).start();
        }

        try {
            barrier.await();
        } catch ( Exception e ) {
            e.printStackTrace();
        }
    }
}

Кирилл Широков 


1Игра слов: $ ⇒ cash ⇒ cache

опубликовал vmrobot ( дек 11 2007, 03:21:24 PM MSK ) Permalink Комментарии [3]

Trackback URL: http://blogs.sun.com/vmrobot/entry/%D0%BA%D0%BE%D0%B3%D0%BE_%D1%80%D0%B0%D0%B7%D0%B1%D1%83%D0%B4%D0%B8%D1%82_hotspot
Комментарии:

Офигенная статья. Желаю "не сдуться" и продолжать в том же духе

опубликовал chand0s Декабрь 12, 2007 at 01:43 PM MSK #

Первый выбор (между побудкой одного и всех) был предложен для умственно отсталых, я так полагаю.

А второй выбор, между FIFO и LIFO, не так очевиден. Благородство оно и в африке оказывается благородством. Да, может последний процесс, ставший в очередь, ещё лежит в кэше, и выполнится быстрее. Но какова вероятность этого? Вероятность застопориться на коротких операциях достаточно мала - именно поэтому монитор так успешно заменятся спинлоком, и это даёт столь заметное ускорение синхронизации в яве. Если же несколько тредов застряли на мониторе - в подавляющем большинстве случаев это длинная операция. И поскольку это длинная операция, и длинное ожидание - в кэше от данных/кода ждущих тредов мало что останется, не зависимо от очерёдности их остановки. Что же мы имеем в результате? Мы имеем увеличение производительности на какие-то проценты (и бодрый рапорт, что теперь наша машинка ещё на миллиметр быстрее конкурента). Но в качестве побочного эффекта этого подхода мы имеем ухудшение обслуживания клиентов в случае большой нагрузки на сервер. При FIFO очереди все задачи будут медленно, но двигаться. При LIFO очереди мы будем иметь постепенно накапливающиеся ждущие треды. Одни треды будут выполнятся быстро, а остальные будут просто стоять. Вместо тянучки получится мёртвая пробка.

В общем, умение держать критическую нагрузку (фактически показатель качества продукта) опять принесено в жертву маркетингу (пылью в глаза в веде бенчмарков). Как обычно.

опубликовал Maxim Kizub Декабрь 12, 2007 at 11:33 PM MSK #

Не совсем тогда понятно, зачем в java реализовано notifyAll(), все-равно реально-то блокирует только один. Кстати, говоря об оптимизациях, интересно все-же знать, оценку прироста производительности.

опубликовал null Декабрь 17, 2007 at 06:44 PM MSK #

Опубликовать комментарий:

Имя
E-Mail:
URL:

Ваш комментарий:

HTML Syntax: Отключен

Хиты страниц за сегодня: 10