В распределенных вычислениях общий объект моментального снимка — это тип структуры данных , которая совместно используется несколькими потоками или процессами. Для многих задач важно иметь структуру данных , которая может обеспечить согласованное представление состояния памяти. На практике оказывается, что невозможно получить такое согласованное состояние памяти, просто обращаясь к одному общему регистру за другим, поскольку значения, хранящиеся в отдельных регистрах, могут быть изменены в любой момент в ходе этого процесса. Чтобы решить эту проблему, объекты моментального снимка хранят вектор из n компонентов и предоставляют следующие две атомарные операции: update(i,v) изменяет значение в i -м компоненте на v , а scan() возвращает значения, хранящиеся во всех n компонентах. [1] [2] Объекты моментального снимка можно создавать с использованием атомарных однозаписывающих многочитающих общих регистров .
В общем, различают объекты моментальных снимков single-writer multi-reader (swmr) и объекты моментальных снимков multi-writer multi-reader (mwmr). В объекте моментального снимка swmr количество компонентов соответствует количеству процессов, и только одному процессу P i разрешено писать в ячейку памяти i , а всем остальным процессам разрешено читать память. Напротив, в объекте моментального снимка mwmr всем процессам разрешено писать во все позиции памяти, а также читать память.
Общая память разделена на несколько частей. Каждая из этих частей содержит одно значение данных. В случае одиночной записи и множественного чтения каждому процессу P i назначена позиция памяти i , и только этому процессу разрешено записывать в позицию памяти. Однако каждому процессу разрешено читать любую позицию в памяти. В случае множественной записи и множественного чтения ограничение изменяется, и любому процессу разрешено изменять любую позицию памяти. Любой процесс P i {1,..., n } в системе из n процессов может выполнять две операции над объектом снимка: scan() и update(i,v) . Операция сканирования не имеет аргументов и возвращает согласованное представление памяти. Операция update(i,v) обновляет память в позиции i значением v .
Оба типа операций считаются происходящими атомарно между вызовом процессом и возвратом памятью. Говоря более обобщенно, в векторе данных каждая запись d k соответствует аргументу последней линеаризованной операции обновления , которая обновляет часть k памяти. [1]
Чтобы получить все преимущества от общих объектов моментальных снимков, с точки зрения упрощения проверок и конструкций, к построению объектов моментальных снимков добавлены два других ограничения. [1] Первое ограничение является архитектурным, означающим, что любой объект моментального снимка создается только с регистрами single-writer multi-reader в качестве базового элемента. Это возможно для снимков single-writer multi-reader. Для объектов моментальных снимков multi-writer multi-reader можно использовать регистры multi-reader multi-writer , которые, в свою очередь, могут быть созданы из регистров single-writer multi-reader. [1] [3] [4]
В распределенных вычислениях построение системы обусловлено целью, чтобы вся система достигала прогресса во время выполнения. Таким образом, поведение процесса не должно приводить к остановке всей системы ( свобода от блокировок ). Более сильная версия этого свойства — свобода от ожидания , означающая, что ни один процесс не может помешать другому процессу завершить свою работу. В более общем смысле это означает, что каждая операция должна завершиться после конечного числа шагов независимо от поведения других процессов. Очень простой алгоритм моментального снимка гарантирует общесистемный прогресс, но является только свободным от блокировок. Этот алгоритм легко расширить, так что он станет свободным от ожидания. Алгоритм Афека и др. [1] , представленный в разделе Реализация, обладает этим свойством.
Существует несколько методов для реализации общих объектов моментальных снимков. Первый представленный алгоритм обеспечивает принципиальную реализацию объектов моментальных снимков. Однако эта реализация обеспечивает только свойство lock-freedom . Вторая представленная реализация, предложенная Afek et al. [1], имеет более сильное свойство, называемое wait-freedom . Обзор других реализаций дан Fich. [2]
Основная идея этого алгоритма заключается в том, что каждый процесс, выполняющий scan()
операции, считывает все значения памяти дважды. Если алгоритм считывает точно такое же содержимое памяти дважды, никакой другой процесс не изменил значение между ними, и он может вернуть результат. Процесс, выполняющий операцию update(i,v)
, просто обновляет свое значение в памяти.
функция scan() пока истина a[1..n] := собирать; b[1..n] := собирать; если (∀i∈{1, .., n} местоположение i не было изменено между его чтениями в течение двух сборок)) то return b; // двойной сбор успешен конец петли
обновление функции (i, v) М[i] := v;конец
Этот алгоритм обеспечивает очень простую реализацию объектов моментальных снимков. Он гарантирует, что система продолжит работу, в то время как отдельные потоки могут голодать из-за поведения отдельных процессов. Процесс P i может помешать другому процессу P j завершить scan()
операцию, всегда изменяя свое значение между двумя сборами памяти. Таким образом, алгоритм является безблокировочным , но не безожидательным . Чтобы удержать это свойство сильнее, ни один процесс не может голодать из-за поведения других процессов. Рисунок 1 иллюстрирует проблему. Пока P 1 пытается выполнить scan()
операцию, второй процесс P 2 всегда нарушает «двойной сбор». Таким образом, процесс сканирования всегда должен перезапускать операцию и никогда не может завершиться и голодать.
Основная идея алгоритма моментального снимка swmr Афека и др. заключается в том, что процесс может определить, изменил ли другой процесс свое местоположение в памяти, и что процессы помогают друг другу. Чтобы определить, изменил ли другой процесс свое значение, к каждому регистру присоединяется счетчик, и процесс увеличивает счетчик при каждом обновлении. Вторая идея заключается в том, что каждый процесс, который обновляет свое местоположение в памяти, также выполняет scan()
операцию и предоставляет свой «вид памяти» в своем регистре другим процессам. Процесс сканирования может заимствовать этот scan
результат и возвращать его.
Используя эту идею, можно построить алгоритм без ожидания , который использует регистры неограниченного размера. Процесс, выполняющий операцию обновления, может помочь процессу завершить сканирование. Основная идея заключается в том, что если процесс видит, что другой процесс обновляет область памяти дважды, этот процесс должен был выполнить полную, линеаризованную операцию обновления между ними. Чтобы реализовать это, каждая операция обновления сначала выполняет сканирование памяти, а затем атомарно записывает значение снимка вместе с новым значением v и порядковым номером. Если процесс выполняет сканирование памяти и обнаруживает, что процесс дважды обновил часть памяти, он может «позаимствовать» «встроенное» сканирование обновления, чтобы завершить операцию сканирования . [1]
функция scan() // возвращает согласованный вид памяти для j = 1 to n do moved[j] := 0 end while true do a[1..n] := collect; // собирает (данные, последовательность, представление) тройки b[1..n] := collect; // собирает (данные, последовательность, представление) тройки if (∀j∈{1, ..., n}) (a[j].seq = b[j].seq) then return (b[1].data, ..., b[n].data) // ни один процесс не изменил память else for j = 1 to n do if a[j].seq ≠ b[j].seq then // процесс перемещен if moved[j] = 1 then // процесс уже перемещен ранее return b[j].view; else moved[j] := moved[j] + 1; end end end function
процедура обновления( i , v ) // обновляет регистры значениями данных, обновляет порядковый номер, встроенное сканирование s[1..n] := scan; // встроенное сканирование r i := (v, r i .seq = r i .seq + 1, s[1..n]); конец процедуры
Каждый регистр состоит из поля для значения данных, порядкового номера и поля для результата последнего встроенного сканирования, собранного перед последним обновлением. В каждой операции сканирования процесс P i может решить, изменил ли процесс свою память, используя порядковый номер. Если во время двойного сбора нет изменений в памяти, P i может вернуть результат второго сканирования. Как только процесс замечает, что другой процесс обновил память между ними, он сохраняет эту информацию в перемещенном поле. Если процесс P j дважды изменил свою память во время выполнения scan(), процесс сканирования P i может вернуть встроенное сканирование процесса обновления, которое он сохранил в своем собственном регистре во время своей операции обновления.
Эти операции можно линеаризовать , линеаризовав каждую операцию update() при ее записи в регистр. Операцию сканирования линеаризовать сложнее. Если двойной сбор операции сканирования успешен, операцию сканирования можно линеаризовать в конце второго сканирования. В другом случае — один процесс обновил свой регистр два раза — операцию можно линеаризовать в момент, когда процесс обновления собирал свое встроенное сканирование перед записью своего значения в регистр. [1]
Одним из ограничений представленного алгоритма является то, что он основан на неограниченной памяти, поскольку порядковый номер будет постоянно увеличиваться. Чтобы преодолеть это ограничение, необходимо ввести другой способ определения того, изменил ли процесс свою позицию памяти дважды между ними. Каждая пара процессов взаимодействует с помощью двух регистров single-writer single-reader (swsr), которые содержат два атомарных бита. Перед тем, как процесс начнет выполнять «двойной сбор», он копирует значение своего партнерского процесса в свой собственный регистр. Если процесс-сканер P i замечает после выполнения «двойного сбора», что значение партнерского процесса P j изменилось между ними, это указывает на то, что процесс выполнил операцию обновления памяти. [1]
function scan() // возвращает согласованное представление памяти for j=1 to n do moved[j] := 0 end while true do for j=1 to n do q i,j := r j .p j,i end a[1..n] := collect; // собирает (данные, битовый вектор, переключение, представление) тройки b[1..n] := collect; // собирает тройки (данные, битовый вектор, переключение, представление) if (∀j∈{1, ...,n}) (a[j].p j,i = b[j].p j,i = q i,j ) and a[j].toggle = b[j].toggle then return (b[1].data, ..., b[n].data) // ни один процесс не изменил память else for j=1 to n do if (a[j].p j,i ≠ q i,j ) or (b[j].p j,i ≠ q i,j ) or (a[j].toggle ≠ b[j].toggle) then // процесс j выполнил обновление if moved[j] = 2 then // процесс уже перемещен до этого return b[j].view; else moved[j] := moved[j] + 1; end end end function
procedure update( i , v ) // обновляет регистры значением данных, "состоянием записи" всех регистров, инвертирует бит переключения и встроенное сканирование для j = 1 до n do f[j] := ¬q j,i end s[1..n] := scan; // встроенное сканирование r i := (v, f[1..n], ¬r i .toggle, s[1..n]); конец процедуры
Неограниченный порядковый номер заменяется двумя битами рукопожатия для каждой пары процессов. Эти биты рукопожатия основаны на регистрах swsr и могут быть выражены матрицей M , где процессу P i разрешено писать в строку i и разрешено читать биты рукопожатия в столбце i . Перед тем, как процесс сканирования выполнит двойной сбор, он собирает все биты рукопожатия из всех регистров, считывая свой столбец. После этого он может решить, изменил ли процесс свое значение во время двойного значения или нет. Поэтому процессу просто нужно снова сравнить столбец с изначально считанными битами рукопожатия. Если только один процесс P j дважды записал, во время сбора P i возможно, что биты рукопожатия не изменятся во время сканирования. Таким образом, необходимо ввести еще один бит, называемый «битом переключения», этот бит изменяется при каждой записи. Это позволяет различать две последовательные записи, даже если никакой другой процесс не обновлял свой регистр. Такой подход позволяет заменить неограниченные порядковые номера битами рукопожатия, не меняя ничего в процедуре сканирования.
В то время как процесс сканирования P i использует бит рукопожатия для определения того, может ли он использовать свой двойной сбор или нет, другие процессы также могут выполнять операции обновления. В качестве первого шага они снова считывают биты рукопожатия, предоставленные другими процессами, и генерируют их дополнение. После этого эти процессы снова генерируют встроенное сканирование и сохраняют обновленное значение данных, собранные - дополненные - биты рукопожатия, дополненный бит переключения и встроенное сканирование в регистр.
Поскольку биты рукопожатия эквивалентно заменяют порядковые номера, линеаризация такая же, как и в случае неограниченной памяти.
Конструкция объекта моментального снимка multi-writer multi-reader предполагает, что n процессам разрешено записывать в любое место в памяти, состоящей из m регистров. Таким образом, больше нет корреляции между идентификатором процесса и местом в памяти. Следовательно, больше невозможно связать биты квитирования или встроенное сканирование с полями данных. Следовательно, биты квитирования, память данных и встроенное сканирование не могут храниться в одном и том же регистре, и запись в память больше не является атомарной операцией.
Поэтому update()
процесс должен обновить три разных регистра независимо. Сначала он должен сохранить биты рукопожатия, которые он считывает, затем выполнить встроенное сканирование и, наконец, сохранить свое значение в назначенной позиции памяти. Каждая независимая запись, по-видимому, выполняется атомарно, но вместе они не являются таковыми. Новая update()
процедура приводит к некоторым изменениям в scan()
функции. Теперь недостаточно дважды прочитать биты рукопожатия и собрать содержимое памяти. Чтобы обнаружить начинающийся update
процесс, процесс должен собрать биты рукопожатия во второй раз, после сбора содержимого памяти.
Если двойной сбор не удается, теперь необходимо, чтобы процесс увидел другой процесс, перемещающийся три раза, прежде чем заимствовать встроенное сканирование. Рисунок 3 иллюстрирует проблему. Первый двойной сбор не удается, потому что процесс, update
запущенный до того, как операция сканирования завершает свою запись в память во время первого двойного сбора. Однако встроенное сканирование этой записи выполняется и сохраняется до того, как P 1 начинает сканировать память, и, следовательно, нет допустимой точки линеаризации. Второй двойной сбор не удается, потому что процесс P 2 начинает вторую запись и обновляет свои биты рукопожатия. В сценарии swmr мы теперь заимствуем встроенное сканирование и возвращаем его. В сценарии mwmr это невозможно, потому что встроенное сканирование из второго write
все еще не линеаризовано в пределах интервала сканирования (начало и конец операции). Таким образом, процесс должен увидеть третье изменение от другого процесса, чтобы быть полностью уверенным, что по крайней мере одно встроенное сканирование было линеаризовано в течение интервала сканирования. После третьего изменения одним процессом процесс сканирования может заимствовать старое значение памяти, не нарушая критерий линеаризации.
Базовая представленная реализация общих объектов моментальных снимков Афека и др. требует операций с памятью. [1] Другая реализация Андерсона , которая была разработана независимо, требует экспоненциального числа операций . [5] Существуют также рандомизированные реализации объектов моментальных снимков на основе регистров swmr с использованием операций. [6] Другая реализация Израэли и Ширази, использующая неограниченную память, требует операций с памятью. [7] [8] Израэли и др. показывают в другой работе нижнюю границу низкоуровневых операций для любой операции обновления. Нижняя граница равна , где w — количество обновителей, а r — количество сканеров. Аттия и Рахман представляют детерминированный алгоритм моментальных снимков на основе регистров swmr, который использует операции на обновление и сканирование. [8] Применяя общий метод Израэли, Шахама и Ширази [9], его можно улучшить до алгоритма неограниченного моментального снимка, которому требуются только операции на сканирование и операции на обновление. Существуют дальнейшие улучшения, представленные Иноуэ и др., [10], использующие только линейное число операций чтения и записи. В отличие от других представленных методов, этот подход использует регистры mwmr, а не swmr.
В распределенных вычислениях существует несколько алгоритмов , которые можно упростить при проектировании и/или проверке с помощью общих объектов моментальных снимков. [1] Примерами этого являются проблемы исключения, [11] [12] [13] параллельные системы временных меток, [14] приблизительное соглашение, [15] рандомизированный консенсус [16] [17] и реализации других структур данных без ожидания. [18] С помощью объектов моментальных снимков mwmr также можно создавать атомарные регистры с несколькими записями и несколькими чтениями.
{{cite book}}
: |work=
проигнорировано ( помощь ){{cite book}}
: |work=
проигнорировано ( помощь )