зверь, которого не убьешь одной пулей. Если речь заходит о параллелизме, то я за различные подходы.
Соблазнительно думать, что можно использовать одну программную парадигму для написания параллельных программ и затем реализо-вывать ее. Тогда для всех программ применялась бы одна парадигма. Но я в это не верю. Я считаю, что для одних стилей программирования больше подходит обмен сообщениями, для других - транзакционная память, для третьих - параллелизм данных. Программисту может потребоваться не один подход, а несколько.
Но если вы меня спросите, лучше ли транзакционная память, чем блокировка и переменные условия? Вот это уже сравнение подобного. Мой ответ - да. Мне кажется, транзакционная память заставит забыть и о том, и о другом. Для всяческих счетчиков, многопоточности с разделяемой памятью на многоядерном процессоре - транзакционная память. Но это, разумеется, не единственный способ справляться с параллельными программами.
Сейбел: Я слышал в ее адрес критику такого рода: оптимистический параллелизм не обеспечивает того уровня параллелизма, на который можно рассчитывать. Утверждается, что легко можно оказаться в ситуации, когда выполнение перестает двигаться вперед.
Пейтон-Джонс: Да, нужно заботиться о зависаниях. Вот мой любимый пример: большая транзакция, которая не фиксируется, потому что в этом месте первой совершается другая, маленькая. Аналогией может быть библиотекарь, который наводит порядок в своей библиотеке. Начинается оптимистическая реорганизация. Две трети работы сделано, тут приходит студент и берет книгу. Он успешно фиксирует свою транзакцию, ведь реорганизация библиотеки еще не зафиксирована. Библиотекарь доходит до конца, обнаруживает отсутствие книги: библиотека изменилась за время реорганизации, структура данных неверна, значит, надо начинать все сначала.
Сейбел: Если есть блокировка и переменные условия, все по-другому - библиотекарь запирает библиотеку, и никто не может взять книгу до полной реорганизации. Поглядев на эту схему, вы немедленно сказали бы: “Мы не можем запереть библиотеку, пока не закончим”, - запретив выдачу книг, так что пришлось бы изобретать более сложную схему блокировки.
Пейтон-Джонс: Верно. Надо создать маленькую подбиблиотеку или что-нибудь в этом духе, куда поместить самые ходовые книги, чтобы студенты могли брать их во время реорганизации основной библиотеки. Надо подумать о стратегии решения конкретной задачи и о том, в каком виде ее выразить. Проблема одна и та же в обоих случаях: как реорганизовать библиотеку, не прекращая полностью выдачу книг. После трудной части - придумывания того, как это сделать, - вы думаете о том, как это выразить. И здесь транзакционная память - абсолютный чемпион. Она превосходит и блокировку, и переменные условия для выполнения параллельных программ.
Сейбел: А если я не хочу допускать, чтобы кто-то пришел ко мне за двадцать первым экземпляром самой ходовой книги и оказался запертым? В физическом мире можно представить, что если кто-то приходит за книгой, мы заменяем ее некой заглушкой, которую библиотекарь использует в реорганизации, и когда книга приходит назад, мы возвращаем ее на место заглушки. Но если реорганизовывать библиотеку в мире с транзакционной памятью, придется повторять транзакцию.
Пейтон-Джонс: Но кое-что остается неизменным - шифр книги, верно? Есть несколько способов решить задачу. Например, вы можете сказать, что при работе с заглушкой сама библиотека не меняется, меняется только сама книга. Вы не изменяете ее ключевое поле - только значение, где книга в данный момент находится. И теперь каталог может меняться, где бы книга ни была. Это прекрасно и поддается выражению естественным способом.
В случае транзакционной памяти библиотекарь просматривает все места в памяти, которые считывал, и проверяет, содержат ли они те самые значения, что и при последнем заходе. Поэтому посещенные им ячейки памяти должны содержать ключевое поле книги, определяющее, куда ее положили. Но библиотекарь не читает содержание книги. Он всего лишь проверяет, содержит ли ключевое поле, скажем, число 73.
Но не буду преуменьшать проблему зависания - она довольно коварна. Нужны хорошие профилирующие инструменты, которые указывают, что транзакция не фиксируется, поскольку сталкивается с другой транзакцией. Нужно, чтобы программа не просто втихомолку подвиса-ла, - нужна обратная связь с ней. То же верно и для системы блокировки. Ненавижу эти часики на экране.
Сейбел: Мне кажется, что в программах с блокировкой мы научились снимать ее так быстро, как только возможно, чтобы минимизировать потери от простоев.
Пейтон-Джонс: Да. Но программировать в этом случае сложнее - мелкомодульную блокировку сложно настроить. Мне кажется, одно из больших преимуществ транзакционной памяти в том, что она работает с точностью чрезвычайно мелкомодульной блокировки на основе очень простых принципов.
Вот один из них - в системах с блокировкой этого нет. Я определяю высокоуровневые инварианты: у меня несколько банковских счетов, общая сумма денег на них равна
Сейбел: Поскольку транзакции изолированы друг от друга.
Пейтон-Джонс: Да. Это действительно очень мощный принцип. Можно делать последовательные умозаключения относительно императивного кода, несмотря на параллелизм. Вы обязаны определить высокоуровневые инварианты, но это также полезно для душевного спокойствия: вы знаете, что именно пытаетесь сохранять. Если посреди транзакции встречается исключение, это тоже здорово - оно не может уничтожить инварианты, поскольку транзакция тогда завершается ничем. Просто сказка! И совершенно по-другому теперь можно рассуждать о скорости выполнения - вы удостоверились, что все минимально правильно, теперь надо убедиться, что программа нигде не подтормаживает. Это уже труднее: на сегодня есть только профилирующие инструменты и инструменты точечной обратной связи.
Сейбел: Меня поражает вот что: оптимистический параллелизм время от времени используется в персистентных базах данных, но намного реже по сравнению с параллелизмом на основе блокировки.
Пейтон-Джонс: Ну, транзакционную память можно реализовать несколькими способами, и оптимистический параллелизм - лишь один из них. Можно устраивать блокировку по мере продвижения - это уже больше похоже на пессимистический параллелизм.
Сейбел: Тут есть еще и тот момент, что менеджеры блокировок - самая сложная часть баз данных.
Пейтон-Джонс: Верно. Что касается транзакционной памяти, то нужна уверенность в том, что один человек - или команда - может ее реализовать, а остальные могут ею пользоваться. Чтобы иметь шанс убедиться в качестве работы, можно хорошо заплатить исполнителям и на год запереть их в темной комнатушке.
Но после этого у каждого должна быть возможность воспользоваться результатом его/их труда через простейший интерфейс. И вот это, по-моему, здорово. Мне бы не хотелось, чтобы каждому требовался подобный уровень знаний. Вот пример, который когда-то приводил Морис Херлихи, я только вчера на него ссылался: очередь с двухсторонним доступом, и вам нужно вставлять и удалять элементы.
Последовательная реализация очереди с двухсторонним доступом - это задача, которую проходят в объеме курса бакалавра. Для параллельной реализации с блокировкой по каждому узлу - это тема для научной статьи. Это слишком нелегко, прямо до абсурда. А для транзакционной памяти такую проблему решает студент. Вы просто “заворачиваете” операции “вставить” и “стереть” в одну атомарную операцию - и дело сделано. По-моему, это занятно. Тут есть количественная разница. Сегодня те, кто реализует транзакционную память, должны убедиться, что несколько изменений вносятся в память как единая атомарная операция. Это не так просто - у вас есть только атомарная инструкция “сравнение с обменом”. Надо быть внимательным.
Есть проблемы и с зависанием; здесь надо кое-что придумать на уровне логики приложения, чтобы