Статьи

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

обзор 

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

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

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

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

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

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

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

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

пример

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 байт-код может быть превращен в псевдокод, как это

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.

Рекомендации

Тесты: TSX Haswell и памяти для транзакций (HLE и RTM) из SiSoftware
Fun с Intel® транзакционных синхронизации расширений  от Intel
поддержки транзакционной памяти: speculative_spin_mutex  от Intel
Making Sense продолжений Intel Haswell транзакционных синхронизации  Йохан де Gelas