Статьи

Аппаратная транзакционная память в Java, или почему синхронизация будет крутой снова

обзор

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

Хотя добавить поддержку этого в C и C ++ нетривиально, добавление поддержки к собственному коду, сгенерированному JVM, может быть выполнено без изменения байтового кода.

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

Что такое аппаратная память транзакций и сколько она будет стоить?

Аппаратная транзакционная память существует уже некоторое время, но до недавнего времени она не была основным потоком. Благодаря внедрению Intel поддержки в некоторые из процессоров 4-го поколения i3 / i5 / i7 (Haswell) и процессоров семейства E3-1200 v3 (до 4-ядерных, с одним сокетом ATM) широко доступны для новых машин на базе Intel. Это может быть позже в этом году или в следующем году, прежде чем мы увидим большее количество ядер, что означает, что HTM будет иметь реальное значение. AFAIK, AMD планируют добавить эту функцию в ближайшее время.

Кстати, в системах Vega Azul эта технология используется уже почти десять лет, и я ожидаю, что Azul лучше всего сможет внедрить ее в JVM.

Оборудование, которое вы купите и, возможно, уже сделаете, сделает это. Многие новые модели ноутбуков имеют процессоры Haswell, поскольку они значительно улучшили энергопотребление.

Как это может работать?

Синхронизированные блоки часто используются в Java на индивидуальной основе. Чтобы упростить код, эти блокировки часто гораздо более грубые, чем оптимальные, например, Hashtable блокирует весь объект / карту для любой операции по сравнению с ConcurrentHashMap, который имеет точную блокировку зерна. Писать точную зернистую блокировку гораздо сложнее, и поэтому она более подвержена ошибкам. Цель аппаратной памяти транзакций состоит в том, чтобы поддерживать детализированную блокировку курса, но получить преимущество точной блокировки. Это особенно полезно для кода, где повторная оптимизация кода нецелесообразна.

пример

1
2
3
4
5
6
7
8
private final Map map = new HashMap<>();
 
public synchronized PooledObject acquireObject(String key) {
    PooledObjectobject = map.get(key);
    if (object == null)
        map.put(key, object = new PooledObject());
    return map;
}

Вы можете ожидать общего случая

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

Что бы вы хотели

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

Без HTM синхронизированный блок должен получить блокировку и обеспечить сериализованный доступ, даже если в большинстве случаев это операция чтения.

С HTM байт-код может быть превращен в псевдокод, как это

01
02
03
04
05
06
07
08
09
10
11
12
13
14
public PooledObject acquireObject(String key) {
    int code;
    do {
        xbegin();
        PooledObjectobject = map.get(key);
        if (object == null)
            map.put(key, object = new PooledObject());
        return map;
    } while((code = xend()) == RETRYABLE);
    if (code != DONE) {
        // take corrective action such as
        // obtain a normal lock and repeat
    }
}

Инструкция XEND обозначает конец, где проверяется умозрительный набор для чтения и записи в кеше, чтобы увидеть, был ли какой-либо из них доступ к какой-либо строке кеша, измененной другим ЦП / потоком. Если нет, внесенные изменения фиксируются. В противном случае любые изменения сбрасываются, и цикл можно повторить.

Примечание. Откат транзакции означает отмену изменений и может даже привести к откату создания объекта, если он не имеет значительных побочных эффектов. Если он имеет побочные эффекты, существует инструкция XABORT, которая может инициировать прерывание транзакции, и потребуется выполнить код возврата.

Compare And Swap ограничен 64-битными, каков предел этих транзакций?

Ограничение — это количество строк, которые вы можете сохранить в кеше L1. Это до 32 КБ. Если у вас есть гиперпоточность, это может быть половина, т.е. 16 КБ. Кроме того, кэш-память L1 является 8-способной ассоциативной, поэтому в худшем случае 9 строк кеша, которые хешируют в один и тот же сегмент, могут привести к сбою транзакции. (меньше с гиперпоточностью) Тем не менее, он намного выше и гораздо более гибок, чем 64-битный CAS или 128-битный CAS.

Написание этой структуры блокировки транзакций с отступлением добавляет шаблон и дублирующий код на языке, подобном C.

Вывод

Прелесть этого шаблона в том, что его можно применять к коду Java, который уже скомпилирован и доступен как библиотеки с открытым исходным кодом. В отличие от кода C, который потребует значительных переделок для использования этой функциональности, Java-программы могут использовать HTM без повторной компиляции. То, что нам нужно, это изменение в JVM.

Примечания (некоторые исправления / пояснения к тому, что я сказал ранее)

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

Хотя Intel TSX доступен в некоторых процессорах Haswell, он доступен не во всех процессорах Haswell. Вы должны проверить с Haswell на ARK и посмотреть, что Intel TSX-NI — Да .

Было отмечено, что это может не иметь большого значения для хорошо настроенного кода. Дизайнер Intel для TSX Рави Раджвар (Qav SF Rajwar) представил на QCon SF 2012 тему « Под капотом» с микроархитектурой Intel следующего поколения под кодовым названием Haswell на трассе « Механическая симпатия» . Если вы посмотрите на страницу 29, это говорит мне о том, что мелкозернистый код в любом случае будет хорошо масштабироваться по ядрам и не получит столько же. TSX может помочь, конечно же, блокировка.

Для более технических деталей я предлагаю вам прочитать пост Джила Тене о механической группе симпатий . У него больше личного опыта настройки JVM для поддержки HTM, чем у любого, с кем я встречался.

использованная литература