Вычислительные машины и программное обеспечение
УДК 004.451.2
М.Ю. Кудрин, Е.В. Соколов, А. Г. Тор м а сов
ВЫЯВЛЕНИЕ СОСТОЯНИЙ ГОНКИ С ПОМОЩЬЮ ГРАФА СОВМЕСТНОГО ИСПОЛНЕНИЯ потоков
Состояние гонки (race condition) — это ситуация, возникающая, когда несколько потоков одновременно обращаются к одному и тому же ресурсу, причем хотя бы один из потоков выполняет операцию записи, и порядок этих обращений точно не определен. Как правило, ошибки, вызванные состояниями гонки, трудно обнаружить и исправить.
Обычный способ борьбы с такими ситуациями — поиск разработчиком участков кода в программном продукте, различный порядок исполнения которых может привести к возникновению состояний гонки. Другим способом является внедрение в программный продукт дополнительной функциональности, которая на этапе тестирования позволяет определить, возникло ли состояние гонки в конкретном варианте исполнения программы [ 1). К сожалению, оба способа не дают права утверждать, что возникновение состояний гонки в программе невозможно при любом варианте ее исполнения.
Существуют строгие математические доказательства отсутствия состояния гонки для определенных алгоритмов. Правда, чаще всего рассуждения не носят универсального характера и могут быть применимы только к рассматриваемым алгоритмам (см., например, доказательства для различных алгоритмов в [2]). При анализе нового алгоритма доказательство приходится строить с нуля.
Есть еще одна проблема - помимо состояний гонки, которые нарушают правильную работу программы, существуют те, которые не влияют на корректное решение поставленной задачи (будем в дальнейшем называть их разрешенными).
Требуется некоторый подход, который позволил бы точно сказать, существуют ли в програм-
ме состояния гонки, приводящие к её некорректной работе (в дальнейшем будем называть их неразрешенными состояниями гонки). Кроме того, удобно, если бы этот подход давал возможность быстро определить, какие именно варианты исполнения программы ошибочны. В данной статье такой подход предложен для программ, удовлетворяющих ряду условий.
Постановка задачи
Как было указано выше, критерием неразрешенное™ состояния гонки является его влияние на корректную работу программы. Значит, нам необходим некоторый способ определения корректности исполнения программы. При этом очевидно, что не существует абсолютного понятия корректности работы произвольной программы. Мы можем только проверить, что конкретная программа правильно решает определенную задачу. Иногда существует способ сделать это, анализируя текущее состояние общих ресурсов для потоков исполнения и, возможно, ранее считанные состояния, которые хранят потоки. Будем рассматривать именно этот случай в следующей формулировке:
существует два потока, каждый из которых исполняет заданный набор команд;
одна или несколько общих ячеек памяти; некоторые из операций, выполняемых потоками, затрагивают общие ячейки памяти.
Требуется определить, возникаетли неразрешенное состояние гонки исходя из значений ячеек памяти после завершения работы потоков и, возможно, ранее считанных значений, которые хранят потоки.
Очевидно, что задачу можно решить, перебрав все возможные варианты совместного
исполнения двух потоков. Однако на практике этот метод неприменим из-за сложности перебора. Нетрудно показать, что если каждый из потоков совершает п операций, то нужно выпол-
V 4
нить 0(С"2л) =1
Гп
действий.
Мы рассмотрим подход, который позволяет существенно уменьшить число необходимых действий, гарантируя при этом точное определение наличия либо отсутствия состояний гонки в программе.
Представление работы потоков
Каждый из потоков выполняет некоторую последовательностьоперапий. Исходя из определения состояния гонки логично предположить, что нас будут интересовать операции, так или иначе связанные с разделяемыми ресурсами — в нашем случае обшими ячейками памяти. В зависимости от отношения к общим ячейкам разобьем операции на четыре группы:
• Операции чтения. Поток считывает значение одной из обших ячеек памяти. Обозначение одной операции — Я.
• Операции записи. Поток записывает значение в одну из общих ячеек памяти. Обозначение одной операции — W.
• Операции хранения и модификации ранее считанного значения. Поток хранит либо выполняет некоторые действия (например, арифметические) над ранее считанным значением одной из общих ячеек памяти. Обозначение одной такой операции — V.
• Другие операции. Операции, не входящие ни в один из вышеописанных классов. Обозначение одной операции — X.
В дальнейшем при обозначении операции будем указывать два индекса: верхний соответствует номеру общей ячейки памяти, над которой производится операция, нижний — номеру потока, выполняющего операцию. Например, Я,1 означает, что первый поток считывает значение третьей ячейки. Для операций, которые мы обозначили через X, верхний индекс не имеет смысла, поэтому его можно опускать.
Введем понятие состояния исполнения двух потоков относительно ячейки памяти. Так будем называть вектор, содержащий три компоненты (а, Ь, с), где а —значение ячейки памяти в данный момент времени; Ь — считанное значение
ячейки, которым оперирует первый поток; с — считанное значение ячейки, которым оперирует второй поток. Компоненты этого вектора изменяются при чтении и записи, а также когда один из потоков модифицирует считанное значение. Будем считать, что при операции записи в ячейку памяти переносится именно это модифицированное значение. До запуска потоков данный вектор может быть записан в виде (т, —, —), где т - начальное значение ячейки памяти, а прочерки означают, что ни один из потоков еще не считывал значение этой ячейки.
Запишем правила, по которым изменяется состояние исполнения потоков относительно ячейки памяти с номером / при выполнении операций:
И7,(а, Ь, с) ->■ (а, а, с), если / =/'; ^(я, Ь, с) -»(а, Ь, а), если / =у;
Ь, с) -> (Ь, Ь, с), если =/; ^(д, Ь, с) -> (с, Ь, с), если /' =у; У,(/)(а, Ь, с) (а,ЛЬ), с), если / =/, Ъ, с) -> (а, Ь,Дс)), если / =/, Х\(а, Ь, с) (а, Ь, с), если / =у, к = 0,1; 0^(а, Ь, с) -> (а, Ь, с), если / *у, к = 0,1, О — любая из операций Я, XV, V, X.
Предположим, что первый поток совершает некоторую операцию А-7,, а второй поток — операцию В*. Возможны два варианта: операция будет выполнена раньше операции В* и операция А\ будет выполнена позже операции В*. Возникает вопрос, в каких случаях состояние исполнения потоков после выполнения операций может быть различным.
Лемма 1. Состояние исполнения потоков относительно ячейки памяти с номером / после выполнения двух операций может зависеть от их порядка только том в случае, если у = к = /и выполнено одно из следующих условий:
А = Я, В = (первый поток считывает значение ячейки памяти, а второй пишет туда же);
А = Щ В = Я (второй поток считывает значение ячейки памяти, первый поток записывает в эту же ячейку);
А = \У, В = (оба потока выполняют операцию записи в одну ячейку).
До казател ьство. Еслиу"*/,тоА,(а,/>,£•)-» -» (а, Ь, с), а значит, состояние исполнения потоков после выполнения А\ и В*2 будет одинаковым вне зависимости от порядка операций. Аналогично рассматривается случай для к * /. Итак ,]—к =/'.
Г
Рассмотрев все варианты операций А и В, нетрудно убедиться, что только в трех случаях итоговое состояние исполнения может быть различным. Запишем его в неопределенных коэффициентах (а, р и у), принимающих значения О и 1. Подстановка нуля должна давать итоговое состояние при одном порядке операций, подстановка единицы — при другом.
А = Я, В =
К'] (а, Ь, с) (о, а, с), ХУ^о, а, с) -> (с, а, с);
Ща, Ь, с) -> (с, Ь, с), И',(с, Ь, с) (с, с, с). Итоговое состояние исполнения — (с, а а + ас, с).
А = \Ч В = Я:
Ч/\(а, Ь, с) (Ь, Ь, с), ЩЬ, Ь, с) (Ь, Ь, с);
Ь, с) -> (а, Ь, а), Щ(а, Ь, а) -> Ь, а). Итоговое состояние исполнения — (Ь,Ь, ра + р/>).
А = XV, В =
Ч/\(а, Ь, с) (А, Ь, с), ЩЬ, Ь, с) (с, А, с);
6, с) (с, 6, с), Ь, с) (М, с). Итоговое состояние исполнения — (уА + ус,Ь,с).
Определение 1. Если существует хотя бы одна общая ячейка памяти, такая, что состояние исполнения потоков относительно нее после выполнения двух операций может зависеть от их порядка, то будем называть эти операции неком-мутирующими, в противном случае скажем, что они коммутируют между собой.
Построение графа совместного исполнения потоков
Пусть первый поток совершает Л: операций, а второй — п операций. Под графом совместного исполнения потоков будем понимать ориентированный граф
С: = (У,А), У= и V,,
<-цы
А= и и {У\,уг\Л
1=1.* |=|,*+| /=|,л+| >=1.1
Пример подобного графа представлен на рис. 1.
В таком графе существует только одна вершина V}, в которую не входит ни одной дуги. Будем называть её начальной вершиной. Вершину V*;!, единственную, из которой не исходит ни одной дуги, назовём конечной. Для произвольной вершины Уу назовем ее уровнем число, равное / + у. Начальная вершина имеет минимальный уровень в графе, конечная вершина — максимальный. Кроме того, нетрудно заметить, что для любых двух смежных вершин в данном графе
Рис. 1. Пример графа совместного исполнения потоков при к = 4. п = 3
уровни различаются на единицу, причем меньше уровень той вершины, откуда исходит дуга. Отсюда, в частности, следует, что в таком графе ггет циклов. В дальнейшем, рассматривая графы совместного исполнения потоков, будем опускать изображения стрелок на дугах. Интерес к подобному графу обусловлен следующим утверждением.
Лемма 2. Назовем ориентированный путь из начальной вершины в конечную полным путем. Существует взаимно-однозначное соответствие между вариантами совместного исполнения потоков и полными путями в соответствующем графе совместного исполнения потоков.
Доказательство. Рассмотрим некоторый вариант совместного исполнения потоков. Будем с троить соответствующий полный путь из вершины следующим образом: на т-м шаге для вершины V/, в путь включается дуга (Уу, '), если т-я операция в варианте исполнения принадлежит первому потоку, и дуга (»/у, Уу+|), если т-я операция в варианте исполнения принадлежит второму потоку. Так как первый поток всего выполняет к операций, а второй — л, то последней вершиной будет V**'. Аналогично, если у нас есть некоторый полный путь, то в соответствующем варианте совместного исполнения т-я операция будет принадлежать первому потоку, если т-я дуга имеет вид ( у*,, у/ 4'), и второму, если т-я дуга
вида (Уу, А раз последняя вершина у**,1 , то всего будет рассмотрено к операций первого потока и пвторого.
Определение 2. Исходя изданного построения будем говорить, что дуги (Уу, у/+1), где у = I, п + 1, представляют /-ю операцию, выполняемую первым потоком. Дуги (Уу, где / = 1, к + 1, представляюту'-ю операцию, выполняемую вторым потоком. Полный путь представляет соответствующий ему вариант совместного исполнения потоков.
Для любых двух некоммутирующих операций / и у поставим знак "+" в области, ограниченной дугами у/+|), (у/+|, у;:1,) и (Уу, Уу+|). На рис. 2 такими операциями являются Я, и
Определение классов эквивалентности
Рассмотрим два различных пути на графе совместного исполнения потоков из вершины в конечную вершину у**'. Пусть (о, Ь, с) — состояние исполнения потоков относительно некоторой ячейки памяти. Проделаем следующее: будем двигаться по дугам графа вдоль каждого из путей и преобразовывать состояние исполнения потоков согласно операциям, которые эти дуги представляют. Если, дойдя до вершины у**,1, мы получим одинаковые состояния, и это верно при рассмотрении любой из общих ячеек памя-
Рис. 2. Пример указания некоммутирующих операций на графе
ти, то будем считать эти пути эквивалентными. Таким образом, все пути из в у**,1 разбиваются на классы эквивалентности. Очевидно следующее утверждение.
Утверждение 1. Два пути из вершины в вершину у**,', которые отличаются друг от друга только парой дуг - (у/, у/+|), (уу'+1, у'Д' ) в одном пути и (Уу, Уу+|), (Уу+|, V,;') вдругом, принадлежат одному классу эквивалентности, если /-я операция первого потока иу'-я операция второго коммутируют между собой (в области, ограниченной этимидугами (у*у, ууж), (Уу'+|, (Уу+|, Уу+'О, (Уу> Уу41на графе совместного исполнения потоков не указан знак "+").
Нас прежде всего интересуют классы эквивалентности полных путей.
Построение представителей классов эквивалентности
Пусть задан некоторый граф совместного исполнения потоков. Определим для него функцию/ /,у), 1 <1<к+ 1,1 <у'2л + 1. Здесь к — число операций, выполняемых первым потоком, а п — число операций, выполняемых вторым потоком.
1Ш>У) = 1 при ¡= к+ 1,У= 1 ,...,п+ 1;
2)//,у)=1 приу = л+ 1,/= 1.....,к;
3)//,у) =/' + 1,У) +Ли+ 1), если /'-я операция первого потока и у'-я операция второго — некоммутирующие, в противном случае//,у) =
=/< +1 ,л+ли+ 1)-Л/+ 1,у+1).
Теорема 1. Число классов эквивалентности полных путей равно/1,1) и может быть вычислено за О(к-п) операций.
Доказательство. Покажем, что функция//, у) равна числу классов эквивалентности путей из вершины у*7 в вершину у**,1. Утверждение очевидно для п. 1 и 2, так как из таких вершин существует только один путь в вершину у**,1.
Рассмотрим п. 3 для коммутирующих операций. Пусть//+ 1,у),//,у' + 1),//+ 1,у+ 1) известны и равны числу классов эквивалентности для вершин \>-'1, Уу+1, у'Д'. Будем пользоваться следующим очевидным утверждением: если два пути из вершины у)1'| в вершину V**} эквивалентны, то и пути, полученные присоединением дуги (у/+|, ) к данным путям, также будут эквивалентными. Аналогично для дуги (у'у+), И наоборот, присоединение таких дуг к неэквивалентным путям порождает неэквивалентные
пути. Это может быть неверно в некоторых частных случаях, но результатом будет лишь увеличение числа классов эквивалентности для анализа.
Представим число классов эквивалентности путей из вершины vJ'+1 в видеД/ + 1,У) = а, + (рис. 3). Здесь о, — число классов эквивалентности, в которых все пути проходят через вершину (равно нулю, если такой вершины не существует или таких классов нет); Ьх — число классов эквивалентности, в которых хотя бы один путь проходит через вершину у}+'|- Но тогда Ь1 должно быть равной» + 1 ,У + 1) согласно утверждению о присоединении дуги, а значит а, =Д/ + 1, /) — Д/+ 1,У + 1). Аналогично для вершины у*,, | число классов эквивалентности, в которых все пути проходят через вершину у^+2, будет равно Д/, у + 1)—Д/ + 1,У + О- Осталось заметить, что число классов эквивалентности для вершины в которых хотя бы один путь проходит через будет равной/ + 1,у'+ 1),так как ¡-я операция первого потока иу'-я операция второго коммутируют между собой.
А значит, Д/,у) =Д/ + 1 ,У)-Д/ + 1,7+1) +
+ли+!)-//+ 1,У+ 1)+Л'+ 1,У+ 1)=Л'+1,7) + +Д/,У+ 1)-Л' + 1,У+1)-
Случай некоммутирующих операций рассматривается аналогично за исключением того, что число классов эквивалентности для вершины Уу, в которых хотя бы один путь проходит через уД, будет равно 2Д/ + 1,У+ 1), так как при-
Рис. 3. Коммутирующие операции
соединение ребер (г/, v/+'), (у/4 ', У,?,) и (v^, v^,), (Vy+i, v,Vi) теперь порождает неэквивалентные пут и. Значит, вэтомслучаеД/',у)=Д/+ 1,у)—Д/ +1,
У+1) +д/,у+1) -Л'+ 1J+ 1) + 2Д/ + 1 ,У + 1) =
=Д/+1,у)+Д/,У+1)-
Опишем теперь алгоритм нахожденияЛ 1,1). Нетрудно заметить, что для нахождения//,/), где / +у = с, нам достаточно знанияД/, т) при всевозможных I + т = с + 1 и / + т = с+ 2. Кроме того, из пунктов 1 и 2 нам известны ДА: + 1, л), Дк, п + 1) и Дк + 1, п + I). Значит, мы можем последовательно вычислить Д/, у), рассмотрев сначала случай /' +j = k+n, затем i +j = k+ n — 1 ит. д. В итоге мы получимД 1,1), при этом в силу такого построения число действий будет О(кп). Теорема доказана.
Предположим, что классы эквивалентности занумерованы числами от единицы доД 1,1), и опишем алгоритм построения пути, принадлежащего 5-му классу эквивалентности. После выполнения алгоритма множество Vsсодержит вершины, принадлежащие некоторому пути изя-го класса эквивалентности. Алгоритм:
1. Vs = 0,1 = 1,у = 1, class = s
2. while (/'< я + 1) I
3. while (1) {
4. = Vsu v'f,
5. if (/ == к + 1 || Д/ + 1 ,У) < class) break;
6. /=/+1;
7. >
8. if(/< « + 1)
9. class = class - (Д/, j) -Д», j + 1));
10. J =>+ 1;
П. }
Не-фудно заметить, что построение пути требует О (п + к)действий.
Оценка числа классов эквивалентности
Представим число операций, выполняемых
первым потоком, в следующем виде: м
к - X ri + w! + х! + v<- Здесь М— число общих ячеек памяти, для t-й ячейки г,', w/, х/, у,1 — количество операций чтения, записи, хранения значений
и других соответственно. Для второго потока
м
п - ]>]r,2 + w; + X? + vj. Используя лемму 2, мож-i=i
но найти число некоммутирующих операций (чис-
м
ZI 2 2 1 12 Г1 И / + rt Wi + w, .
/=1
Теорема 2. Для числа классов эквивалентности полных путей Ссправедливо
и
1 + £ r>; + r;w] + и>,2 < с <
M
<2*YJ riwf+rf2wl+w!wi•
Доказательство. Покажем, что превращение некоторой пары коммутирующих операций в некоммутирующие увеличивает число классов эквивалентности не меньше чем на единицу и не более чем в два раза. Действительно, рассмотрим пути, принадлежащие некоторому классу эквивалентности. Если хотя бы в двух из них порядок дуг, представляющих указанные операции, различен, то класс разбивается надва новых. Покажем теперь, что хотя бы один класс будет разбит. Рассмотрим два пути из утверждения 1 прих= \,у=\ и », j, совпадающих с номерами операций. До превращения операций эти пути принадлежали одному классу эквивалентности, после - разным. Осталось заметить, что при отсугствии пар некоммутирующих операций класс эквивалентности будет единственным. Теорема доказана.
Рассмотрим пример. Пусть« =к=5, число неком-
м
мутирующее операций ^ r'w~ + + w'< vt'< =
Максимальное число классов эквивалентности полных путей 16, минимальное — 5 (рис. 4). Общее же число вариантов полных путей
,5 Ю!
■ = 252.
5!5!
Построение редуцированного |рафа и анализ результатов
В силу определения классов эквивалентности нам достаточно вычислить результирующие состояния исполнения только для одного представителя каждого из классов. Удалим из графа дуги, которые не входят в полные пути, соответствующие выбранным представителям классов. Мы получим редуцированный граф совместного исполнения потоков. При этом результирующее состояние исполнения должно остаться тем же самым. Вычислим его в неопределенных коэффициентах для всех ячеек памяти, двигаясь от начальной вершины графа к конечной. Для того чтобы определить, возможно ли возникновение неразрешенного состояния гонки, нам остается только проанализировать полученное состояние.
Часто признаком правильной работы программы является независимость некоторых компонент результирующего состояния от выбора варианта исполнения потоков (например, в одной из ячеек памяти после работы потоков все-
Рис. 4. Максимальное и минимальное число классов эквивалентности
гда должна быть записана некоторая константа с). Тогда, если существует два набора неопределенных коэффициентов, таких, что их подстановка дает различный результат, то можно утверждать, что возможно возникновение неразрешенного состояния гонки.
Были исследованы две простые задачи. В случае задачи об изменении значения ячейки в двух потоках число классов эквивалентности оказалось равным четырем при общем числе вариантов исполнения, равном 20. В случае задачи о
транзакции число классов эквивалентности также равно четырем при общем числе вариантов исполнения 28.
В работе был описан подход, позволяющий гарантированно обнаружить все состояния гонки в программе, приводящие к ее неправильной работе. При этом необходимые расчеты минимизированы за счет объединения вариантов исполнения потоков программы в классы и проведения вычислений только для одного представителя каждого из классов.
СПИСОК ЛИТЕРАТУРЫ
1. Serebryany К. Data race test. URL: http:// code.google.com/p/data-race-test.
2. Heriihy M., Shavit N. The An of Multiprocessor Programming. Elsevier, 2008.
3. Бурков В.H., Заложнев А.Ю., Новиков Д.А. Теория графов в управлении организационными системами. М.: Синтег, 2001.
4. Rahul V. Patil, George B. Concurrency: Tools And Techniques to Identify Concurrency Issues // MSDN Magazine, June 2008.
5. Philip N. Klein, Hsueh Lu, Netze R. Detecting Race Conditions in Parallel Programs that Use Semaphores. Springer Berlin/Heidelberg, 2008.
УДК 62 1.37
Д.А. Крупенко
АДАПТАЦИЯ СИСТЕМЫ ПРОЕКТИРОВАНИЯ AUTOCAD ДЛЯ РАЗРАБОТКИ КОНСТРУКЦИЙ МИКРОСБОРОК СВЧ
Современная микроэлектроника достигла такого уровня сложности, что изделия стали узкоспециализированными и системно-ориентированными, в связи с этим постоянно растут потребности в разработке новых устройств, позволяющих решать частные задачи. Так, в области проектирования СВЧ-уст-ройств огромное значение приобретает разработка гибридных интегральных схем, в частности их разновидности, так называемых микросборок.
Микросборка, или электронный модуль СВЧ, — это изделие электронной техники для диапазона частот 3—30 ГГц, имеющее законченное конструктивное исполнение и состоящее из одного или нескольких функциональных узлов СВЧ, взаимозаменяемое и неремонтопригодное в условиях эксплуатации. Пример модуля СВЧ изображен на рис. 1.
Особенность микросборок состоит в том, что они представляют собой изделия частного применения, т. е. изготовляются для конкретного типа аппаратуры.
Создание модуля предполагает разработку его конструкции и технологии изготовления. К современным модулям СВЧ предъявляется большое число сложных, часто взаимоисключающих требований, среди которых:
высокий уровень электрических параметров с учетом конструктивно-технологических запасов;
прочность и устойчивость к внешним воздействующим факторам — механическим, климатическим и специальным;
надежность и длительная сохраняемость; минимальные габариты, установочные и присоединительные размеры и масса;
приемлемые способы охлаждения и крепления в аппаратуре;
Г