← Назад в библиотеку

Источник: Воробьёв Л.О. Автоматизация трассировки многопоточных программ / Л.О. Воробьёв . // Материалы VI Международной научно-технической конференции Современные информационные технологии в образовании и научных исследованиях (СИТОНИ-2019) . — Донецк : ДонНТУ , 2019. — С. 252–255.


Автоматизация трассировки многопоточных программ

Воробьёв Л.О.
ГОУ ВПО Донецкий национальный технический университет (г. Донецк)

Рассмотрены основные аспекты отладки многопоточности и проведена аналогия с сложности программирования с принципом неопределённости Гейзенберга.

Введение

Порой возникает необходимость ускорить вычисления при решении задач прогнозирования, обработки графической информации и др. Для этого помогает распараллеливание задачи на несколько вычислительных машин. Грубо говоря, виртуальный компьютер [1] и есть та машина, которая выполняет один поток операций. Многопоточной [2] называется программа, которая выполняет сразу несколько потоков инструкций, т. е. последовательностей операторов.

Для выполнения многопоточных программ используется несколько арифметико-логических устройств (АЛУ). Следует учитывать, что программа выполняется тогда, когда ей предоставлен квант времени планировщиком задач. Это создает сложность в проверке корректности программы, которая заключается в том, что многопоточная программа выполняется с определенными задержками на каждом потоке.

Если программа небольшая, то проверить все возможные случаи фрагментации программы на кванты времени не составит труда.

При увеличении размера программы сложность её проверки возрастает экспоненциально, поскольку задержка может произойти после любой инструкции и длится неопределенное количество квантов.

Для проверки многопоточной программы применяется тестирование по методу белого ящика [3], когда исходный код программы записывается в дизассемблированном виде и нумеруется инструкции. Затем в таблице трассировки с несколькими столбцами для каждого потока инструкций проверяется работа программы. Все варианты задержек перебрать на бумаге нереально, поэтому включается интуитивный подход. Выделяется фрагмент кода, который начинается и заканчивается одной из инструкций:

Затем проверяется, что будет если задержка произойдет после первой инструкции, затем после второй и так до предпоследней; при этом варьируется длительность задержки; процесс повторяется для случаем, когда выполняется два, три или более потоков.

В общем задача заключается в том, чтобы пользователь вводил что-то наподобие модульных тестов для результатов трассировки. А система проверяла соответствие каждого случая этому требованию.

Как происходит отладка многопоточных программ на самом деле. В код программы вставляется отладочная информация. При выполнении программы она записывает в текстовый файл журнал своих действий: номер потока и что он сделал. Этот журнал содержит ценную информацию о том, какая последовательность действий дает сбой.

Иными словами, журнал ведётся для того, чтобы несколько рабочих записывали туда свои действия, а потом начальник выяснял, почему результат оказался неправильным. Каждый рабочий по отдельности выполняет свою работу правильно. А если их несколько, то происходит рассогласование.

Задержка между инструкциями равносильно тому, что рабочий отвлекается от своей работы, например ответ на телефонный звонок. При этом неизвестно, после какого действия зазвонит телефон. Другой рабочий может тоже отвлекаться. При этом рабочих может быть больше двух.

Журнал нужен, чтобы выяснить, какая инструкция дала сбой. А затем проинструктировать рабочих так, чтобы их рассинхронизация не влияла на результат работы. Ведь никогда не знаешь, когда планировщик задач вытеснит поток с процессора. Поэтому нужно просчитать все варианты.

Если программа маленькая, то можно воспользоваться тестированием по методу черного ящика. Для этого запускается программа несколько тысяч раз. Допустим, в одной из тысячи итераций планировщик задач создал задержку в такой момент, что программа зашла в тупик. Задача тестирования по методу чёрного ящика состоит в том, чтобы найти все критические места в программе, задержка в которых может нарушить её работу.

При написании многопоточных программ следует учитывать, что:

Физическая природа многопоточных программ

Многопоточная событийно-ориентированная программа с обширным глобально изменяемым состоянием проявляет все признаки неопределенности в квантовой механике [4]. При попытке отладки такой программы её поведение меняется и ошибки невозможно обнаружить. Нечто похожее есть и в квантовой механике. Принцип неопределённости Гейзенберга гласит, что при попытке увеличить точность измерения одной величины точность измерения другой уменьшается. Законы квантовой механики находят свое отражение в программном обеспечении.

Физика не контролирует процесс ядерной реакции. Радиоактивный распад ядра Урана – полностью случайное явление. Внутри ядра атома действует принцип неопределённости: нельзя с точностью определить значение какой-либо величины, не изменив при этом значение другой. Поэтому физика не может предсказать, в какой момент рванет ядерный реактор.

Аналогично дело обстоит в многопоточной программе. Программа, в которой выполняются много потоков – это ядерный реактор. И если его не контролировать, то он рано или поздно выйдет из строя. В ядерном реакторе для его контроля есть графитовые стержни, которые замедляют цепную ядерную реакцию. А в многопоточной программе роль таких стержней играет синхронизация.

Так же, как и в таблице Менделеева существует конечное число элементов, существует ограниченное число высокоуровневых конструктов многопоточности, которые нормально работают. По аналогии с радиоактивностью химического элемента можно судить о подверженности многопоточной программы взаимной блокировке и гонке данных. Потому использовать примитивы синхронизации можно только для реализации высокоуровневых синхронизационных структур. Внутри такой структуры, чем больше строк кода, тем больше вероятность того тем меньше надежность программы. Поэтому необходимо до предела сокращать размер конструктов.

Ядро атома состоит из протонов и нейтронов. По аналогии с примитивами синхронизации, если баланс протонов и нейтронов нарушается, то ядро становится неустойчивым, т. е. радиоактивным. Изотоп существует некоторое время, потом распадается. Программа работает, а потом попадает в неопределенное поведение,если в ней допущены ошибки синхронизации. Чтбы этого избежать, следует использовать библиотеки многопоточности, которые просто работают.

Выводы

Отладку многопоточных программ можно автоматизировать с помощью полного перебора всех вариантов задержки между такими инструкциями:

Для каждого такого варианта выполнения потоков проверяется условие корректности, заданное программистом либо тестировщиком на основании общепринятого контракта данной синхронизационной структуры.

Эффективность трассировки зависит от количества строк кода: чем их меньше, тем больше скорость перебора всех вариантов задержки.

Часто бывает, что исправить многопоточную программу можно только полностью переписав её. Чем объёмнее многопоточная программа, тем больше вероятность того, что она работает неправильно. Поэтому необходимо стремиться сократить число атомарных операций в каждой подпрограмме.

Литература

  1. Корнеев В.Д. Параллельное программирование в MPI / В.Д. Корнеев . — Новосибирск : Изд-во ИВМиМГ СО РАН , 2002. — 215 с. — URL: http://lib.yar.ru/yarcln/edoc/... .
  2. Гасанов З.З. Анализ производительности многопоточных программ, написанных на языках Java и Go / З.З. Гасанов . // Наука и образование сегодня . — № 6 (29). — 2018. — С. 25–27. — URL: https://cyberleninka.ru/articl... (дата обращения: 12.12.2019) .
  3. Степанченко И.В. Методы тестирования программного обеспечения: Учебное пособие / И.В. Степанченко . — Волгоград : ВолгГТУ , 2006. — 74 с. — URL: http://window.edu.ru/resource/... .
  4. Мишель Д. Почему они не умеют писать многопоточные программы: пер. с англ. [Электронный ресурс]. — 2014. — URL: https://dev.by/news/pochemu-on... .
← Назад в библиотеку