В компьютерной науке и технике транзакционная память пытается упростить параллельное программирование , позволяя группе инструкций загрузки и сохранения выполняться атомарным способом . Это механизм управления параллелизмом, аналогичный транзакциям базы данных для управления доступом к разделяемой памяти в параллельных вычислениях . Системы транзакционной памяти предоставляют высокоуровневую абстракцию как альтернативу низкоуровневой синхронизации потоков. Эта абстракция позволяет координировать между параллельными чтениями и записями разделяемых данных в параллельных системах. [1]
В параллельном программировании синхронизация требуется, когда параллельные потоки пытаются получить доступ к общему ресурсу. Низкоуровневые конструкции синхронизации потоков, такие как блокировки, пессимистичны и запрещают потокам, находящимся за пределами критической секции , запускать код, защищенный критической секцией. Процесс применения и снятия блокировок часто функционирует как дополнительные накладные расходы в рабочих нагрузках с небольшим количеством конфликтов между потоками. Транзакционная память обеспечивает оптимистичное управление параллелизмом , позволяя потокам работать параллельно с минимальным вмешательством. [2] Цель систем транзакционной памяти — прозрачно поддерживать области кода, отмеченные как транзакции, путем обеспечения атомарности , согласованности и изоляции .
Транзакция — это набор операций, которые могут выполнять и фиксировать изменения, пока нет конфликта. При обнаружении конфликта транзакция вернется в свое начальное состояние (до любых изменений) и будет перезапускаться до тех пор, пока все конфликты не будут устранены. До успешного завершения результат любой операции является чисто спекулятивным внутри транзакции. В отличие от синхронизации на основе блокировок, где операции сериализуются для предотвращения повреждения данных, транзакции допускают дополнительный параллелизм, пока несколько операций пытаются изменить общий ресурс. Поскольку программист не несет ответственности за явную идентификацию блокировок или порядка, в котором они получены, программы, использующие транзакционную память, не могут создавать тупик . [2]
При наличии этих конструкций транзакционная память обеспечивает высокоуровневую абстракцию программирования, позволяя программистам заключать свои методы в транзакционные блоки. Правильные реализации гарантируют, что данные не могут быть разделены между потоками без прохождения транзакции и создания сериализуемого результата. Например, код может быть написан как:
def transfer_money ( from_account , to_account , amount ): """Перевести деньги с одного счета на другой.""" с транзакцией (): from_account . balance -= amount to_account . balance += amount
В коде блок, определенный как «транзакция», гарантированно атомарен, согласован и изолирован базовым внедрением транзакционной памяти и прозрачен для программиста. Переменные внутри транзакции защищены от внешних конфликтов, что гарантирует, что либо передается правильная сумма, либо не выполняется никаких действий вообще. Обратите внимание, что ошибки, связанные с параллелизмом, все еще возможны в программах, которые используют большое количество транзакций, особенно в программных реализациях, где библиотека, предоставляемая языком, не может обеспечить правильное использование. Ошибки, внесенные через транзакции, часто бывает трудно отладить, поскольку точки останова не могут быть размещены внутри транзакции. [2]
Транзакционная память ограничена тем, что требует абстракции разделяемой памяти. Хотя программы транзакционной памяти не могут создавать тупик, программы все равно могут страдать от динамической блокировки или нехватки ресурсов . Например, более длинные транзакции могут многократно возвращаться в ответ на несколько более мелких транзакций, тратя время и энергию. [2]
Абстракция атомарности в транзакционной памяти требует аппаратного механизма для обнаружения конфликтов и отмены любых изменений, внесенных в общие данные. [3] Аппаратные транзакционные системы памяти могут включать модификации процессоров, кэша и протокола шины для поддержки транзакций. [4] [5] [6] [7] [8] Спекулятивные значения в транзакции должны быть буферизированы и оставаться невидимыми для других потоков до времени фиксации. Большие буферы используются для хранения спекулятивных значений, избегая распространения записи через базовый протокол когерентности кэша . Традиционно буферы были реализованы с использованием различных структур в иерархии памяти, таких как очереди хранения или кэши. Буферы, расположенные дальше от процессора, такие как кэш L2, могут содержать больше спекулятивных значений (до нескольких мегабайт). Оптимальный размер буфера все еще является предметом споров из-за ограниченного использования транзакций в коммерческих программах. [3] В реализации кэша строки кэша обычно дополняются битами чтения и записи. Когда контроллер оборудования получает запрос, контроллер использует эти биты для обнаружения конфликта. Если из параллельной транзакции обнаружен конфликт сериализуемости, то спекулятивные значения отбрасываются. При использовании кэшей система может ввести риск ложных конфликтов из-за использования гранулярности строк кэша. [3] Load-link/store-conditional (LL/SC), предлагаемый многими RISC- процессорами, можно рассматривать как самую базовую поддержку транзакционной памяти; однако LL/SC обычно работает с данными, которые имеют размер собственного машинного слова, поэтому поддерживаются только транзакции с одним словом. [4] Хотя аппаратная транзакционная память обеспечивает максимальную производительность по сравнению с программными альтернативами, в настоящее время наблюдается ограниченное использование.
Программная транзакционная память обеспечивает семантику транзакционной памяти в программной библиотеке времени выполнения или языке программирования [9] и требует минимальной аппаратной поддержки (обычно атомарной операции сравнения и обмена или эквивалента). Как недостаток, программные реализации обычно имеют потерю производительности по сравнению с аппаратными решениями. Аппаратное ускорение может снизить некоторые из накладных расходов, связанных с программной транзакционной памятью.
Из-за более ограниченной природы аппаратной транзакционной памяти (в текущих реализациях) программное обеспечение, использующее ее, может потребовать довольно обширной настройки, чтобы в полной мере воспользоваться ее преимуществами. Например, динамический распределитель памяти может оказывать значительное влияние на производительность, а также заполнение структуры может влиять на производительность (из-за проблем с выравниванием кэша и ложным разделением); в контексте виртуальной машины различные фоновые потоки могут вызывать неожиданные прерывания транзакций. [10]
Одной из самых ранних реализаций транзакционной памяти был буфер хранения gated, используемый в процессорах Crusoe и Efficeon компании Transmeta . Однако он использовался только для облегчения спекулятивных оптимизаций для двоичной трансляции, а не для какой-либо формы спекулятивной многопоточности или предоставления ее напрямую программистам. Azul Systems также реализовала аппаратную транзакционную память для ускорения своих Java -приложений, но это также было скрыто от посторонних. [11]
Sun Microsystems реализовала аппаратную транзакционную память и ограниченную форму спекулятивной многопоточности в своем высокопроизводительном процессоре Rock . Эта реализация доказала, что ее можно использовать для обхода блокировок и более сложных гибридных систем транзакционной памяти, где транзакции обрабатываются с помощью комбинации аппаратного и программного обеспечения. Процессор Rock был отменен в 2009 году, как раз перед приобретением Oracle ; хотя фактические продукты так и не были выпущены, ряд прототипов систем был доступен исследователям. [11]
В 2009 году AMD предложила Advanced Synchronization Facility (ASF), набор расширений x86 , которые обеспечивают очень ограниченную форму поддержки аппаратной транзакционной памяти. Цель состояла в том, чтобы предоставить аппаратные примитивы, которые можно было бы использовать для высокоуровневой синхронизации, такие как программная транзакционная память или алгоритмы без блокировок. Однако AMD не объявила, будет ли ASF использоваться в продуктах, и если да, то в какие сроки. [11]
Совсем недавно, в 2011 году, IBM объявила, что Blue Gene/Q имеет аппаратную поддержку как транзакционной памяти, так и спекулятивной многопоточности. Транзакционная память может быть сконфигурирована в двух режимах; первый — неупорядоченный и одноверсионный режим, в котором запись из одной транзакции вызывает конфликт с любыми транзакциями, читающими тот же адрес памяти. Второй режим предназначен для спекулятивной многопоточности, обеспечивая упорядоченную многоверсионную транзакционную память. Спекулятивные потоки могут иметь разные версии одного и того же адреса памяти, а аппаратная реализация отслеживает возраст каждого потока. Младшие потоки могут получать доступ к данным из более старых потоков (но не наоборот), а записи по тому же адресу основаны на порядке потоков. В некоторых случаях зависимости между потоками могут привести к прерыванию работы младших версий. [11]
Расширения транзакционной синхронизации Intel ( TSX) доступны в некоторых процессорах Skylake . Ранее они также были реализованы в процессорах Haswell и Broadwell , но оба раза реализации оказывались дефектными, и поддержка TSX была отключена. Спецификация TSX описывает API транзакционной памяти для использования разработчиками программного обеспечения, но не содержит подробностей о технической реализации. [11] Архитектура ARM имеет похожее расширение. [12]
Начиная с GCC 4.7 доступна экспериментальная библиотека для транзакционной памяти, которая использует гибридную реализацию. Вариант Python PyPy также вводит транзакционную память в язык.