Русская Википедия:Программная транзакционная память
Шаблон:Rq В компьютерных технологиях, программная транзакционная память (Шаблон:Lang-en) представляет собой механизм управления параллелизмом, аналогичный механизму транзакций баз данных для управления доступом к совместно используемой памяти в параллельных вычислениях. Это альтернатива для синхронизации на основе блокировки. Транзакция в этом контексте является частью кода, который выполняет считывание и запись в разделяемую (совместно используемую) память. Считывание и запись логически происходит в единичный момент времени, а промежуточные состояния невидимы для других (результативных) транзакций. Идея обеспечения транзакций аппаратной поддержкой зародилась в 1986 году в работе и патенте Тома Найта.[1] Идея получила публичное освещение благодаря Морису Херлихи и Элиоту Моссу.[2] В 1995 году Нир Шавит и Дэн Тойту дополнили эту идею до программной транзакционной памяти (SТМ). STM по-прежнему находится в центре интенсивных исследований; возрастает её поддержка для практических реализаций.
Характеристика
В отличие от методов блокировки, используемых в большинстве современных многопоточных приложений, STM очень оптимистична: поток завершает изменения разделяемой памяти без учёта того, что делают другие потоки, и регистрирует любое считывание и запись в лог. Вместо того, чтобы использовать записывающее устройство для проверки, не оказывает ли он отрицательного влияния на другие действующие операции, ответственность передаётся считывающему устройству, которое после завершения полной транзакции проверяет, не произвели ли другие потоки параллельно изменения в памяти, к которой был получен доступ в прошлом. Эта последняя операция, в которой проверяются изменения транзакций и которая, если проверка успешна, остаётся неизменной, называется фиксацией. Транзакция может прекратиться в любое время, в результате чего все последние изменения будут отменены. Если транзакция не может быть совершена из-за конфликтов изменений, она прерывается и повторно выполняется сначала до тех пор, пока результативно не завершится.
Преимущество такого оптимистического подхода возрастает благодаря параллелизму: ни одному потоку не нужно ожидать получения доступа к ресурсу, и разные потоки могут одновременно и безопасно модифицировать непересекающиеся части структуры данных, которые защищались бы одним локом.
Однако на практике SТМ-системы проигрывают в производительности мелкомодульным системам, основанным на блокировках, на небольшом количестве процессоров (от 1 до 4 в зависимости от приложения). Это связано в первую очередь с накладными расходами на поддержку лога и с затрачиваемым временем на совершение транзакций. Но даже в этом случае производительность отличается не более, чем в 2 раза.[3] Сторонники STM считают, что такие потери оправданы концептуальными преимуществами SТМ.
Теоретически, временная и пространственная сложности выполнения n параллельных транзакций в худшем случае - O(n). Фактические затраты зависят от реализации (можно отменить транзакцию на ранней стадии, чтобы избежать накладных расходов), но всегда будут случаи, хоть и редкие, когда lock-алгоритмы будут иметь лучшую временную сложность, чем программная транзакционная память.
Концептуальные преимущества и недостатки
В дополнение к преимуществам производительности, STM значительно упрощает концептуальное понимание многопоточных программ и помогает их удобному сопровождению, слаженно работая с существующими высокоуровневыми абстракциями, такими как объекты и модули.
Lock-программирование содержит ряд известных проблем, которые часто возникают на практике:
- Важно помнить о перекрывающихся операциях и частичных операциях в разделённых и кажущихся несвязанными частях кода — задача очень тяжёлая и чреватая ошибками.
- Оно требует от программистов осваивать политику блокировки во избежание тупиков (Deadlock, Лайвлоков) и других проблем управления процессами. Такая политика часто приводится в исполнение произвольным образом и бывает ошибочна, и когда возникают проблемы, их трудно воспроизвести и отладить.
- Это может привести к инверсии приоритетов — явлению, при котором высокоприоритетный поток вынужден ожидать низкоприоритетный поток, имеющий исключительный доступ к необходимому ресурсу.
Напротив, концепция транзакционной памяти гораздо проще, потому что каждая транзакция может рассматриваться по отдельности, как однопоточное вычисление. Тупики или предотвращаются полностью, или разрешаются внешней программой управления транзакций; программисту вряд ли нужно беспокоиться об этом. Инверсия приоритетов по-прежнему может быть проблемой, но высокоприоритетные транзакции могут прервать конфликтующие низкоприоритетные операции, которые ещё не были совершены.
С другой стороны, необходимость прерывания неудавшихся транзакций также накладывает ограничения на их поведение: они не могут выполнять любые операции, которые не могут быть отменены, в том числе большинство вводов-выводов. Такие ограничения, как правило, на практике преодолеваются путём создания буферов, которые ставят в очередь необратимые операции и выполняют их спустя некоторое время вне какой-либо транзакции. В языке Хаскель это ограничение приводится в исполнение системой типов во время компиляции.
Компонуемые операции
В 2005 году Тим Харрис, Саймон Марлоу, Саймон Пейтон-Джонс и Морис Херлихи описали STM-систему, созданную на Хаскеле, реализующем параллелизм. Эта система позволяет произвольным атомарным операциям компоноваться в более крупные атомарные операции — полезная концепция, невозможная при lock-программировании. По словам авторов:
Шаблон:Начало цитаты «Пожалуй, наиболее фундаментальный недостаток в том, что lock-программы не могут компоновать: корректные фрагменты могут не сработать, будучи скомпонованными. Рассмотрим, например, хеш-таблицу с потокобезопасными операциями вставки и удаления. Теперь предположим, что мы хотим удалить один элемент из таблицы t1 и вставить его в таблицу t2, но промежуточное состояние (в котором ни одна таблица не содержит этот элемент)не должно быть видимым для других потоков. Пока конструктор хеш-таблицы не определит данную необходимость, просто не существует способа удовлетворить этому требованию. В общем, каждая корректная операция (вставки, удаления) не могут быть скомпонована в более крупные корректные операции» Шаблон:Конец цитаты
С STM эта проблема решается просто: простое объединение двух операций в одной транзакции превращает компонуемую операцию в атомарную. Единственным камнем преткновения является то, что для абонента, который не знает деталей реализации методов компоновки, неясно когда они должны попытаться повторно выполнить транзакцию, если она не производится. В ответ на это, авторы предложили команду повторной попытки, которая использует журнал транзакций (log-файл), генерируемый неудавшейся транзакцией для определения считываемого ею участка памяти. Тогда она снова автоматически запускает данную транзакцию, когда один из этих участков памяти меняется. Это основывается на логике, что транзакция не будет вести себя иначе, пока хотя бы одно такое значение не изменилось.
Авторы также предложили механизм построения альтернатив (функция илиИначе — orElse). Он запускает одну транзакцию и, если транзакция делает повторную попытку, запускает вторую. Если то же происходит и со второй, механизм запускает их обе снова, пока не произойдут существенные изменения. Данная функция, сопоставимая с функцией сетевого стандарта POSIX select(), позволяет вызывающему ожидать любое из ряда событий одновременно. Она также упрощает программирование интерфейсов, например, путём предоставления простого механизма конвертации между блокирующими и неблокирующими операциями.
Эта схема была реализована в компиляторе языка Хаскель GHC.
Предлагаемый вспомогательный язык
Концептуальная простота SТМ-систем позволяет программисту легко работать с ними, используя относительно простой синтаксис языка. В своей книге «Вспомогательный язык для лёгких транзакций» Тим Харрис и Кейр Фрейзер предложили идею использования классической условной критической области (CCR) для представления транзакций. В своей простейшей форме это всего лишь «атомарный блок», участок кода, который последовательно выполняется в единичный момент времени:
// Атомарная вставка узла в двусвязный список atomic { newNode->prev = node; newNode->next = node->next; node->next->prev = newNode; node->next = newNode; }
По достижении конца блока транзакция совершается, если это возможно, иначе прекращается и повторяется. Условные критические области также допускают условие сохранения, позволяющее транзакции ожидать выполнения, пока действует её задание.
atomic (queueSize > 0) { remove item from queue and use it }
Если условие не выполняется, программа управления (менеджер) транзакций будет ждать, пока не совершится другая, которая повлияет на условие прежде, чем повторит попытку. Такая свободная связь между производителями и потребителями совершенствует модульный принцип по сравнению с чётким сигнализированием между потоками. Книга «Компонуемая операция обращения к памяти» пошла дальше со своей командой повторной попытки (см. выше), которая может в любое время прервать транзакцию и ожидать, пока не произойдёт какое-либо изменение значения, ранее считанного данной операцией, перед повторением попытки. Пример:
atomic { if (queueSize > 0) { remove item from queue and use it } else { retry } }
Эта способность динамического повторения в конце транзакции упрощает модель программирования и открывает новые возможности.
Одной из проблем является поведение исключений, когда они распространяются за пределы транзакций. В труде " Компонуемая операция обращения к памяти " авторы решили, что это должно прервать транзакцию, так как исключения обычно указывают на неожиданные ошибки в языке Хаскель (с параллелизмом), но что это исключение может хранить предоставленную информацию и считывать её во время транзакции в целях диагностики. Они подчёркивают, что разумны также и другие проектные решения при других параметрах.
Транзакционная блокировка
SТМ может быть реализована как алгоритм без блокировок и с блокировками. Есть два типа блокировки.
- Блокировка при столкновении операций (Эналса, Саха и Харриса), при которой записи в память осуществляются, сначала временно блокируя данную область памяти, непосредственно записывая значения и регистрируя их в журнале регистрации откатов операций.
- Блокировка при совершении транзакции, которая блокирует ячейки памяти только во время совершения фазы.
Схема совершения транзакции, названная «Транзакционная блокировка-2» и реализованная Дайсом, Шалевым и Шавитом, использует глобальное время. Каждая транзакция начинается с чтения текущего значения времени и хранит его для чтения. Затем при каждом чтении и записи версия определённой области памяти сравнивается с версией для чтения, и, если оно больше, то транзакция отменяется. Это гарантирует, что код выполнится на соответствующей копии памяти. Во время совершения все области чтения заблокированы, и значения данной версии всех областей памяти для записи и чтения повторно проверяются. Наконец, глобальное время увеличивается, новые значения записи из журнала записываются обратно в память с указанием новой версии времени.
Всё более популярным методом управления транзакционными конфликтами в транзакционной памяти, особенно в SТМ, является Шаблон:Не переведено 3 (СО). Он используется для достижения упорядочиваемости безблокировочно (то есть без блокировки при конфликте операций и только с блокировкой при совершении транзакции) при помощи изменения порядка совершения транзакций (например, Рамадан и соавторы, 2009, и Жанг и соавторы, 2006). Упорядочиваемость является основой для корректного состояния транзакционной памяти (при выполнении параллельных транзакций). Уже были опубликованы десятки статей и патентов об STM с применением « порядка совершения».
«Жанг и соавторы, 2006» — патент США, несущий название «Программное обеспечение порядка совершения транзакций и управление конфликтами» (который ссылается на патент США 5701480 о порядке совершения). Приведём выдержки:
"Разрабатываются различные технологии и методы для применения порядка совершения в системе программной транзакционной памяти. Система программной транзакционной памяти снабжена функцией, чтобы предопределённый порядок совершения был применим для множества операций. Предопределённый порядок совершения используется во время выполнения, чтобы установить порядок, в котором совершать транзакции в системе программной транзакционной памяти. Процесс управления конфликтами вызывается, когда происходит конфликт между первой и второй транзакциями. Предопределённый порядок совершения используется в процессе управления конфликтами, чтобы определить, какая из транзакций должна выиграть конфликт и получить разрешение на продолжение".
С порядком совершения желаемое свойство упорядоченности осуществляется путём совершения транзакций только в хронологическом порядке, совместимом с порядком по приоритету (как определено хронологическим порядком операций в конфликтах)
Реализации
Шаблон:Список примеров SRTM было реализовано (разного качества и стабильности) на разных языках программирования. Таких, как:
C/C++
- TBoost.STM (formerly DracoSTM) Совместные усилия CU-Boulder и Boost Libraries Group создали для C++ STM библиотеку, в первую очередь автором которого является Justin E. Gottschlich и Jeremy G. Siek.
- TinySTM основанная на времени STM и Tanger для интеграции STM с C и C++ через LLVM.
- Lightweight Transaction Library (LibLTX), реализация для С, (автор Robert Ennals) основной акцент сделан на эффективность. Реализация основывается на его статьях «Software Transactional Memory Should Not Be Obstruction-Free» и «Cache Sensitive Software Transactional Memory».
- LibCMT, реализация с открытым кодом для C, сделанная Duilio Protti и основанная на «Composable Memory Transactions». Эта реализация также включает C# binding.
- TARIFA является прототипом, который реализует ключевое слово «atomic» в C/C++.
- Intel STM Compiler Prototype Edition реализация STM для C/C++ непосредственно в компиляторе (Intel Compiler) для Linux или Windows, генерирующем 32 или 64 битный код для процессоров Intel и AMD. Реализует ключевое слово «atomic», а также предоставляет способы декорации определения функций (declspec) для управления / разрешения на использование в «atomic» секциях.
- stmmap реализация STM в C, основанная на разделяемой памяти. Предназначена для совместного использования памяти между потоками и / или процессами (а не только между потоками внутри процесса) с семантикой транзакций. В C++ реализована многопоточная версия данного аллокатора.
- CTL реализация STM в C, основанная на TL2, но со многими расширениями и оптимизациями.
- Several implementations by Tim Harris & Keir Fraser, основанная на идее из статей «Language Support for Lightweight Transactions», «Practical Lock Freedom», и предстоящей неопубликованной работе.
- RSTM University of Rochester STM написанная командой учёных под руководством Michael L. Scott.
- G++ 4.7 уже поддерживает STM для C/C++ прямо в компиляторе. Эта возможность до сих пор числится экспериментальной, но обеспечивает необходимую для тестирования функциональность.
C#
- SXM, реализация для C# Microsoft Research. Documentation, Download pageШаблон:Недоступная ссылка.
- LibCMT, реализация с открытым кодом, (Duilio Protti) базируемая на «Composable Memory Transactions». Реализация также включает C# binding.
- NSTM, .NET Software Transactional Memory, написанное полностью на C#, предлагает вложенные транзакции и даже интеграцию с System.Transactions.
- MikroKosmos A Verification-Oriented Model Implementation of an STM in C#.
Clojure
- Clojure поддержка STM встроена в ядро языка.
Common Lisp
- CL-STM мультиплатформенная реализация STM для Common Lisp.
Haskell
- STM библиотека, как сказано в «Composable Memory Transactions», является частью Haskell Platform.
Java
- SCAT research group реализация AtomJava.
- JVSTM реализует концепцию Versioned Boxes, предложенную João Cachopo и António Rito Silva, членами Software Engineering Group — INESC-ID
- XSTM является открытым исходным кодом для Java и .NET с расширяемой архитектурой. XSTM реализован в виде библиотеки, а также предоставляет расширения для уведомления об изменениях, настойчивости и репликации объектов.
- Deuce Среда разработки для Java Software Transactional Memory, использующая байт-код.
- Multiverse Java 1.6+ основанная на Software Transactional Memory (STM). Эта реализация, использует Multi Version Concurrency Control (MVCC) в качестве параллельного механизма контроля.
- DSTM2 Sun Lab’s Dynamic STM библиотека.
- ObjectFabric дистрибутив STM.
OCaml
- coThreads и одновременно библиотека программирования OCaml, предлагает STM (originally STMLib) в качестве модуля. Как и любой другой компонент в этой библиотеке, STM модуль может быть использован вместе с VM-level threads, системой потоков и процессов.
Perl
- STM для Perl 6 был реализован в Pugs через Glasgow Haskell Compiler's STM библиотеку.
Python
- Durus простая, но полная и быстрая, STM реализация для Python, что позволяет использовать STM внутри одного процесса и STM в server/multiple клиент архитектуре. В дополнение к встроенной памяти формата существуют и другие, например Berkeley DB доступна здесь.
- Fork of CPython with atomic locks Шаблон:Wayback — Armin Rigo объясняет свой патч для CPython в an email to the pypy-dev list.
- pypy-stm Шаблон:Wayback — надстройка над PyPy c рабочей реализацией интерпретатора Python 2.7, поддерживающая одновременное исполнение нитей существующих многопоточных приложений на разных ядрах CPU.
Scala
- ScalaSTM Лёгкая библиотечная STM для Scala.
- RadonSTM STM для Scala, которая была реализована как часть проекта Activate Framework
Smalltalk
- GemStone/S [1] Transactional Memory Object Server для Smalltalk.
Другие языки
Примечания
Ссылки
- Morry Katz, PARATRAN: A transparent transaction based runtime mechanism for parallel execution of Scheme, MIT LCS, 1989
- Nir Shavit and Dan Touitou. Software Transactional Memory. Proceedings of the 14th ACM Symposium on Principles of Distributed Computing, pp. 204–213. August 1995. The paper originating STM.
- Maurice Herlihy, Victor Luchangco, Mark Moir, and William N. Scherer III. Software Transactional Memory for Dynamic-Sized Data Structures. Proceedings of the Twenty-Second Annual ACM SIGACT-SIGOPS Symposium on Principles of Distributed Computing (PODC), 92-101. July 2003.
- Tim Harris and Keir Fraser. Language Support for Lightweight Transactions. Object-Oriented Programming, Systems, Languages, and Applications, pp. 388–402. October 2003.
- Tim Harris, Simon Marlow, Simon Peyton Jones, and Maurice Herlihy. Composable Memory Transactions. ACM Symposium on Principles and Practice of Parallel Programming 2005 (PPoPP’05). 2005.
- Robert Ennals. Software Transactional Memory Should Not Be Obstruction-Free.
- Michael L. Scott et al. Lowering the Overhead of Nonblocking Software Transactional Memory gives a good introduction not only to the RSTM but also about existing STM approaches.
- Torvald Riegel and Pascal Felber and Christof Fetzer, A Lazy Snapshot Algorithm with Eager Validation introduces the first time-based STM.
- Dave Dice, Ori Shalev, and Nir Shavit. Transactional Locking II.
- Knight, TF, An architecture for mostly functional languages, ACM Lisp and Functional Programming Conference, August, 1986.
- Knight, TF, System and method for parallel processing with mostly functional languages, US Patent 4,825,360, April, 1989.
- Ali-Reza Adl-Tabatabai, Christos Kozyrakis, Bratin Saha, Unlocking concurrency, ACM Queue 4, 10 (December 2006), pp 24–33. Ties multicore processors and the research/interest in STM together.
- James R Larus, Ravi Rajwar, Transactional Memory, Morgan and Claypool Publishers, 2006.
- Леонид Черняк. Транзакционная память — первые шаги // «Открытые системы» , № 04, 2007
- ↑ Tom Knight. An architecture for mostly functional languages. Шаблон:Wayback Proceedings of the 1986 ACM conference on LISP and functional programming.
- ↑ Maurice Herlihy and J. Eliot B. Moss. Transactional memory: architectural support for lock-free data structures. Proceedings of the 20th annual international symposium on Computer architecture (ISCA '93). Volume 21, Issue 2, May 1993.
- ↑ Шаблон:Cite web