elizarov


Блог Романа Елизарова


Previous Entry Share Next Entry
Транзакционная память от Intel для x86
elizarov

Мы стоим на пороге новый эры в многопоточном программировании. Компания Intel анонсировала примитивы для транзакционной синхронизации в будущей микроархитектуре Haswell выход которой в свет планируется в 2013 году. Есть все шансы, что он станет первым процессором с подобным механизмом, который доступен широкой публике. Там будет реализована поддержка аппаратного устранения блокировки с помощью префиксов XACQUIRE/XRELEASE и аппаратные транзакции в памяти с помощью инструкций XBEGIN/XEND/XABORT. Оба этих подхода становятся возможными благодаря тому, что в процессоре появится механизм, который позволит ему поддерживать множество прочитанных и множество измененных кэш-линий, отслеживать возникающие конфликты (два доступа из разных транзакций, один из которых на запись), и, что важно, сохранять все изменения в рамках транзакции в основную память атомарно с точки зрения других процессоров или все их отменять.

Аппаратное устранение блокировки позволит существенно упростить написание быстрых и масштабируемых многопоточных структур данных, с минимальным изменением в стиле программирования и подхода к их дизайну. Отличный пример с хэш-таблицами приведен в этой статье на сайте Intel. Правильно спроектированные для многопоточного доступа структуры данных, типа хэш-таблиц, в которых не происходит фактических конфликтов при доступе к разным данным, можно будет защищать общей грубой блокировкой, используя префикс XACQUIRE при его взятии и XRELEASE при его освобождении. В этом случае процессор будет выполнять код критической секции оптимистично, без взятия блокировки, и автоматически перезапускать операцию с блокировкой только в случае фактического обнаружения конфликтующего доступа в память.

Инструкции для аппаратных транзакций XBEGIN/XEND/XABORT разработаны так, чтобы их можно было бы подружить с теми или иными подходами по программной реализации транзакций в памяти. В инструкции XBEGIN указывается место в коде, с которого должно быть продолжено исполнение в случае отмены аппаратной транзакции. Я уверен, что этот анонс существенно подстегнет исследования в области транзакционной памяти. Ведь сейчас, несмотря на существенно количество экспериментальных языков, поддерживающих транзакции в памяти, они практически не используются на практике, в силу огромных накладных расходов. Грубо говоря, если накладной расход на типичную программную реализацию транзакций в памяти замедляет ваш код в десятки раз, то использовать его на стандартных серверных системах с теми же десятками ядрами не имеет практического смысла. А аппаратные транзакции позволяют программировать многопоточный код с транзакциями практически без дополнительных накладных расходов, при условии что количество измененных и прочитанных кэш-линек в транзакции не превосходит некоторый аппаратный предел. Про численное значение пределов для первых процессоров Haswell пока нет подробностей, но теоретически можно ожидать поддержку в рамках всего L1 кэша процессора с учетом его ассоциативности.

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


  • 1
я почему то все больше начинаю уважать MISC процессора

ну насчет практически не используемых языков, тут не совсем верная информация. STM не выстрелила для мутабельных языков, но те же Haskell & Clojure, которые немутабельные по дефолту, вполне себе практически используются. У меня несколько проектов на кложуре и я не вижу слишком большого оверхеда.

Я именно Haskell & Clojure и имел в виду, когда писал эту фразу. Это очень нишевые языки, суммарная доля которых в разработке ПО не превыашает 1%. И даже понятно почему именно так и должно быть, но не будут вдаваться здесь в эти подробности (лучше об этом напишу отдельный пост).

Безусловно, в силу своей заточеннсоти под неименяемые структуры, они лучше всего подходят для STM к раз потому, что если в них и происходит изменения общего сотояния, то очень редко, ибо для этого программисту нужны специальные бубны типа STM монады или refs. А это значит что и накладные расходы на STM в их общей картине мира незаметны. Я даже не говорю о том, что в тех нишах, где эти языки используются, производительность не играет первоочередной роли, ибо сами неизменяемые/функциональные/persistent структуры данные имеют большие накладные расходы по сравнению с изменяемыми. Поэтому программистам на таких языках Intel-овский RTM ничего заметного не даст.

Прочитал спеку по диагонали. Увы, прошли времена, когда программист мог понять, как работает процессор. В спеке написано примерно следующее - "мы вам не скажем, как избежать аборта, но рекомендуем пользоваться антивирусом, чистить кэш браузера и парковать головки жесткого диска перед выключением".

Интересно, как оптимизировать код, если нет никакой гарантии, что выполняемая тобой транзакция не будет абортиться в 100% случаев, приводя в итоге к существенному замедлению вместо ускорения? Профилировать каждую ветку под каждый CPU? Профилировать в рантайме?

Ну это же пока не спека на конкретный процессор, а только спека на инструкции. Будем надеятся, что в спеке на процессор будет подробней всё написано.

А про оптимизации под конкретный CPU. Так это всегда было так. Те, кто занимается оптимизацией, всегда это делали и делают под конкретный процессор.

Роман, вы пишете:
"Правильно спроектированные для многопоточного доступа структуры данных", так может надо писать сразу в три банка памяти параллельно, а читать из трёх мест разными процессорами? Пишет всегда один, остальные подождут. Поток записи всегда меньше. Важнее скорость чтения, мне кажется.

  • 1
?

Log in

No account? Create an account