Транзакционная память

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

Транзакционная память (англ. Transactional memory, TM) — технология синхронизации общей памяти для конкурирующих потоков, позволяющая им выполнять операции сохранения, которые воспринимаются другими потоками как атомарные[1]. Эта технология упрощает параллельное программирование, выделяя целые группы различных инструкций в атомарные транзакции. Конкурирующие потоки работают параллельно, пока не начинают модифицировать одну и ту же область памяти.

Подход к управлению конкурентностью с использованием транзакционной памяти называют оптимистичным[2]: предполагается, что потоки работают независимо друг от друга и в редких случаях меняют одни и те же данные. В таком случае большинство транзакций заканчивается успешно. В противоположность этому, подходы с использованием блокировок называют пессимистичными: предполагается, что потоки часто конфликтуют, и следовательно, им часто запрещается находиться в критической секции одновременно[3].

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

Появление технологии[править | править код]

В базах данных механизм транзакций известен вот уже несколько десятилетий. Изначально идея переноса транзакций из мира баз данных в мир параллельного программирования была высказана Давидом Лометом в 1977 году. А в 1993 году Морис Херли и Элиот Мосс предложили реализовать поддержку транзакционной памяти на аппаратном уровне[4].

Однако, лишь только в 2007 году была заявлена поддержка данной технологии в процессоре Rock[5][6]. Это была одна из первых попыток аппаратной реализации, но в 2010 году после ликвидации компании Sun Microsystems проект был свёрнут. Более менее коммерчески успешная реализация аппаратной транзакционной памяти была впервые представлена фирмой IBM для своих архитектур Power и System Z в 2012 году[7].

Преимущества и недостатки[править | править код]

К преимуществам транзакционной памяти можно отнести

Впрочем, транзакционная память не является универсальным решением всех проблем параллельного программирования. Недостатками подхода являются

  • общее падение производительности — в большей степени справедливо для программных реализаций;
  • вероятность возникновения лайвлоков — в следствие слишком оптимистичного алгоритма блокировок может возникнуть ситуация обратная тупиковой;
  • трудности при использовании нового кода вместе с унаследованным, который не применяет транзакции для доступа к разделяемой памяти — такой код может нарушить согласованность транзакций;
  • ограниченность применения — можно выполнять только обратимые операции[8];
  • сложность отладки — внутри транзакции нельзя установить точку останова[3].

Вообще говоря, транзакции не получится эффективно применить для координации независимых задач, таких как «производитель-потребитель». А транзакционная память сама по себе не является заменой всех остальных видов синхронизации в параллельной программе. Кроме того, неизвестно, как должны вести себя вложенные транзакции, и не понятно, в какой момент нужно делать изменения, произведённые такими транзакциями, видимыми всем остальным[9].

Программные реализации[править | править код]

Эффекта транзакционной памяти можно добиться и с помощью различных программных решений. Такой механизм называется программной транзакционной памятью (англ. software transactional memory) или сокращённо STM.

Среди множества реализаций программной транзакционной памяти можно выделить четыре.

Clojure[править | править код]

Ядро языка Clojure непосредственно поддерживает транзакционную память.

Подход к STM в Clojure называется управлением конкурентным доступом с помощью многоверсионности: сохраняются множественные логические версии данных, используемых в транзакциях. В течение транзакции поток наблюдает снимок данных на момент её начала. Существуют варианты использования конкурентности, при которых среде разрешается «расслабиться» для достижения дополнительной производительности[3][10].

Haskell[править | править код]

Транзакционная память в Haskell содержится в библиотеке STM, которая входит в Haskell Platform. Некорректное использование транзакционных типов определяется на этапе компиляции программы.

Haskell обладает мощной системой типов. Язык разделяет функции с побочными эффектами и чистые функции. Для выполнения любого атомарного действия в Haskell, действие предваряется ключевым словом atomically[3].

Scala[править | править код]

Реализация STM для Scala (ScalaSTM) разрабатывалась под впечатлением от реализаций в Haskell и Clojure. Кроме Scala, ScalaSTM вызывается из Java и Clojure. Реализация используется в популярном фреймворке для написания параллельных программ Akka[3].

Для реализации разделяемой структуры указатели на следующий и предыдущий узел делают потокобезопасными[11].

C / C++[править | править код]

Начиная с версии 4.7, GCC поддерживает транзакционную память. Реализация представляет собой библиотеку времени выполнения libitm, для компиляции указывается флаг -fgnu-tm (-mrtm, -mhle)[12]. Библиотека разрабатывалась с оглядкой на черновик транзакционных конструкций для C++ (предлагается включение в стандарт языка).

Большинство реализаций аппаратной транзакционной памяти используют принцип наибольшего усилия. Поэтому практические реализации используют объединение технологий аппаратной и программной транзакционной памяти. Такие системы называют системами «гибридной транзакционной памяти». К ним относится, в частности, реализация GCC[3].

Аппаратные реализации[править | править код]

Существуют аппаратные реализации в некоторых чипах Intel и IBM[13] [14].

Примечания[править | править код]

  1. Программирование транзакционной памяти AIX. IBM Knowledge Center. Дата обращения 6 января 2018.
  2. Аппаратная транзакционная память. Обзор решений. Часть 1. Intel® Developer Zone (27 сентября 2013). Дата обращения 6 января 2018.
  3. 1 2 3 4 5 6 Борис Егоров. Транзакционная память: история и развитие. Хабрахабр (5 мая 2014). Дата обращения 6 января 2018.
  4. Tim Harris, James Larus, Ravi Rajwar. Mark Hill: Transactional Memory (англ.) (PDF) p.6. Morgan & cLaypool publishers (2010). doi:10.2200/S00272ED1V01Y201006CAC011. Дата обращения 6 января 2018.
  5. Ashlee Vance. Sun slots transactional memory into Rock. Prays for code revolution (англ.). The Register (21 September 2007). Дата обращения 7 января 2018.
  6. Transactional Memory (англ.) (13 August 2007). Дата обращения 7 января 2018. Архивировано 16 августа 2009 года.
  7. Аппаратная транзакционная память. Обзор решений. Часть 3. Intel® Developer Zone (28 сентября 2013). Дата обращения 7 января 2018.
  8. Гарри Триндер. Транзакционная память. MSDN (3 декабря 2009). Дата обращения 6 января 2018.
  9. Джеймс Лярус, Кристос Козиракис. Сергей Кузнецов: Транзакционная память. CitForum (2008). Дата обращения 6 января 2018.
  10. Neale Swinnerton. Clojure STM - What? Why? How? (англ.) (недоступная ссылка) (11 April 2014). Дата обращения 6 января 2018. Архивировано 11 ноября 2016 года.
  11. ScalaSTM - Quick Start (англ.). GitHub. Дата обращения 6 января 2018.
  12. Transactional Memory in GCC (англ.). GCC Wiki. Дата обращения 6 января 2018.
  13. Анонсирована аппаратная поддержка транзакционной памяти в Haswell / Блог компании Intel / Хабр
  14. Транзакционная память и многопоточность / Блог компании IBM / Хабр