1 / 44

Поиск ошибок в многопоточном приложении (на примере Thread Checker )

Поиск ошибок в многопоточном приложении (на примере Thread Checker ). ЛЕКЦИЯ 9, часть 1. ЛИТЕРАТУРА. 99% - по материалам тренинга Intel для преподавателей ВУЗов, апрель 2006, Нижний Новгород, «свободный перевод» Калининой А.П. Цели и задачи. Научиться

lyre
Download Presentation

Поиск ошибок в многопоточном приложении (на примере Thread Checker )

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Поиск ошибок в многопоточном приложении (на примере Thread Checker) ЛЕКЦИЯ 9, часть 1

  2. ЛИТЕРАТУРА • 99% - по материалам тренинга Intelдля преподавателей ВУЗов, апрель 2006, Нижний Новгород, «свободный перевод» Калининой А.П.

  3. Цели и задачи • Научиться • Применять Thread Checker для тестирования правильности работы приложений на основе Windows* threads, поиска и ликвидации разнообразных ошибок, связанных с взаимодействием потоков • Определять, безопасны ли библиотечные функции для многопоточного выполнения (не возникают ли ошибки при параллельном выполнении нескольких функций: если возникают, то «небезопасны»)

  4. Содержание • Что такоеIntel® Thread Checker... • Определение условий возникновения гонок данных (race conditions) • Thread Checker – помощник «многопоточного» программиста • Другие ошибки многопоточного приложения • Проверка библиотеки на безопасность многопоточного выполнения • Другие возможностиThread Checker

  5. Зачем это нужно... • Создание многопоточного приложения может оказаться сложной задачей • Новый класс проблем возникает из-за взаимодействия одновременно работающих потоков (concurrent threads) • Гонки данных или конфликты памяти (Data races or storage conflicts) • Больше, чем один поток имеет доступ к изменению данных без синхронизации измененных значений • Тупики (Deadlocks ) • Потоки ждут события, которое никогда не наступит

  6. Intel® Thread Checker • Инструмент для отладки многопоточных программ • Находит ошибки в многопоточных программах на основе Win32*, POSIX* и OpenMP* • Быстро находит такие ошибки, на выявление которых традиционным способом (по результатам работы многопоточной программы) требуется несколько дней • Находит источник ошибки, а не ее проявления • Ошибка не обязательно должна случиться, чтобы быть выявленной • «Вставлен» в среду VTune™ Performance Analyzer • Тот же самый интерфейс среды VTune™

  7. Как и что можно анализировать с помощью Intel® Thread Checker • Поддерживает несколько различных компиляторов • Компиляторы Intel® C++ и Fortran , версии v7 и выше • Microsoft* Visual* C++, v6 • Microsoft* Visual* C++ .NET* 2002, 2003 & 2005 Editions • Интегрируется в среду Microsoft Visual Studio .NET* • Доступна такая форма представления результатов анализа, как «участки кода, ответственные за ошибку» • Контекстное меню выявленной ошибки: можно сразу получить помощь - щелкнуть на Diagnostic Help • Выявляет причины ошибок и предлагает способы их ликвидации • Предлагает на выбор набор API в качестве определяемых пользователем синхронизационных примитивов

  8. Выявляет причины ошибок и предлагает способы их ликвидации. • Предлагает на выбор набор API в качестве определяемых пользователем синхронизационных примитивов Контекстное меню (щелчок правой кнопкой): выбор помощи по результатам диагностики (Diagnostic Help) Помощь Thread Checker

  9. Thread Checker: выполнение анализа • Способ выполнения анализа: динамически, во время работы приложения • Производится мониторинг: • Потоков и используемых синхронизационных примитивов API • Последовательности выполнения в каждом потоке • Последовательности взаимодействия потоков • Доступа потоков к памяти Анализируемый код должен быть выполнен, чтобы анализ стал возможным

  10. Thread Checker: перед запуском необходимо учесть, что... • Инструментирование: необходимо помнить, что... • Добавляется обращение к библиотеке для записи информации • О работе потоков и объектов синхронизации API • О доступе к памяти • Увеличивается время выполнения и объем выполняемого кода • В тестируемом приложении рекомендуется применять малый объем обрабатываемых данных (workloads) • Время выполнения и объем приложения увеличиваются • Несколько запусков с различными потоками выполнения дадут более полную картину Для конечного результата важно, какого рода «загрузку» Вы осуществили

  11. Требования к загружаемому приложению • Чтобы выделить проблемный код для каждого потока: • Действуйте под девизом: чем меньше, тем лучше! • Минимизируйте набор обрабатываемых данных • Уменьшайте объем изображения... • Минимизируйте количество итераций в цикле или временных шагов • Моделируйте минуты, а не дни... • Уменьшайте скорость обновления значений переменных • Меньше кадров в секунду... Тогда найдете ошибки многопоточного приложения быстрее!

  12. Подготовка приложения для анализа Thread Checker • Компиляция • Используйте многопоточно - безопасные динамические библиотеки (/MD, /MDd) • Включите генерацию символьной информации (/Zi, /ZI, /Z7) • Отключите оптимизацию (/Od) • «Линкование» (Link ) • Нужно сохранить символьную информацию (/debug) • Определить релоцированные секции (Specify relocatable code sections) (/fixed:no)

  13. Бинарное инструментирование (Binary Instrumentation) • Выполняется для поддерживаемых компиляторов • Запуск приложения • Должен быть выполнен из-под Thread Checker • Приложение инструментируется во время выполнения • Также применяются внешние инструментированные динамические библиотеки (DLLs)

  14. Инструментирование исходного кода (Source Instrumentation) • Компиляторы Intel® C++ или Fortran • Компилировать с/Qtcheck • Выполнение приложения • Запуск в среде VTune™ • Запуск из-под командной строки Windows* • Полученные данные размещаются в файле результатов threadchecker.thr • Просмотр результатов (.thr file) в среде VTune • Дополнительные динамические библиотеки (DLLs) не инструментируются и не анализируются Это дает более подробную диагностику

  15. Intel® Thread Checker Wizard Intel® Thread Profiler Wizard Advanced Activity Configuration Intel® Thread Checker Wizard Threading Wizards Выбрать Threading Wizards Выбрать визард Thread Checker Запуск Thread Checker

  16. Диагностики Thread Checker

  17. Сортировка диагностик по группам

  18. Форма представления «участки кода, ответственные за...»

  19. Правой кнопкой мыши. . . Более подробная помощь Помощь по диагностикам

  20. Задание 1 • Откомпилируйте и выполните последовательную версию поиска простых чисел без Thread Checker • Откомпилируйте и выполните многопоточную версиюбез Thread Checker • Выполните приложение в Thread Checker, чтобы определить источники ошибок

  21. S1: A = 1.0; S2: B = A + 3.14; S3: A = 1/3 * (C – D); . . . . . . . . . . . . S4: A = (B * 3.8) / 2.7; Анализ зависимостей Рассмотрим данный последовательный код: • Пусть S1, S2, S3, S4 – различные задания, которые предполагается отдать на выполнение различным потокам. • Но перед тем, как распределить работу, нужно понять, насколько эти задания независимы. • «Потоковая зависимость» (flow dependence) между S1 и S2 • Значение A изменяется в S1 и только после должно использоваться в S2(запись должна быть раньше чтения) • «Противопотоковая» зависимость (anti dependence ) между S2 и S3 • Значение A считывается в S2 раньше, чем изменяется в S3(чтение должно быть раньше записи) • Зависимость «вывода» (output dependence) между S3 и S4 • Вычисление значения A в S3 должно быть раньше вычислений в S4(запись раньше записи)

  22. Зависимости, обнаруженные Thread Checker • Зависимость «вывода» (output dependence) • Конфликты «запись-запись» (Write-Write conflict):один из потоков успевает изменить значение переменной раньше,чем согласно последовательному алгоритму ее должен изменить другой поток • «Противопотоковая» зависимость (Anti-dependence) • Конфликты «чтение-запись» (Read-Write conflict): один из потоков успевает считать значение переменной раньше,чем согласно последовательному алгоритму ее должен изменить другой поток • «Потоковая зависимость» (Flow dependence) • Конфликты «запись-чтение» (Write-Read conflict): один из потоков успевает изменить значение переменной раньше,чем согласно последовательному алгоритму ее должен считатьдругой поток

  23. Условия (Race Conditions) возникновения гонки данных • Не определен жестко порядок выполнения операций • Одновременный доступ к одной переменной нескольких потоков • Это наиболее популярная ошибка в многопоточных программах • Эта ошибка может быть далеко не очевидной и трудно выявляемой

  24. Как уничтожить возможность возникновения гонки данных ... • Решение:Ограничить видимостьпеременных пределами каждого потока • Когда можно ограничить видимость переменных… • Для значений, не используемых вне параллельного региона • Для промежуточных или «рабочих» (“work”) переменных • Как это сделать… • Применять клаузы OpenMP, определяющие пределы видимости (OpenMP scoping clauses (private, shared)) • Описывать переменные в пределах потоковой функции • Выделять память в пределах стека потока (Allocate variables on thread stack) • Сохранять в потоке API (TLS (Thread Local Storage) API)

  25. Как уничтожить возможность возникновения гонки данных ...(продолжение) • Решение:контролировать разделяемый доступ с помощью выделения критических регионов (critical regions) • Когда использовать контролируемый доступ… • Для величин, используемых вне параллельного региона • Для переменных, которые должны изменять несколько потоков • Как это сделать... • Взаимное исключение или синхронизация • «Замок», семафор, событие, критическая секция, атомическая операция (Lock, semaphore, event, critical section, atomic…) • Правило (Rule of thumb): один «замок» (lock) на один элемент данных (здесь и дальше: «замок» - аналогия «блокировки»)

  26. Задание 2 – вычисление интеграла • Поставить комментарий на клаузу privateи изучить реакцию Thread Checker

  27. Возложите на Thread Checker выполнение “черной работы” В помощь «многопоточному программисту»... • Если создаете многопоточную программу, то обязательно... • Явные общие и частные переменные должны быть соответствующим образом распределены между «HANDLEs» потоков • Проанализировали ли Вы зависимости между оставшимися переменными? • Как быть, если объем параллельного кода больше, чем 100 линий? • Разделяемыми или общими являются переменные в вызываемых функциях? • Можете ли Вы проконтролировать, когда указатели ссылаются на одну и ту же область памяти? • Thread Checker – помощник «многопоточного» программиста • Создайте потоковые функции (или OpenMP: параллельные регионы ) • Выполните компиляцию и запуск программы в Thread Checker • Изучите диагностику • Измените директивы и/илиструктуру

  28. Тупики или бесконечные «зависания» (Deadlock) • Возникает, когда поток ждет события, которое никогда не произойдет • Чаще всего причины тупиков связаны с нарушением иерархической структуры «замков» (блокировок) • Правильный порядок: сперва «наложить замок» затем «снять замок» (lock and un-lock in the same order) • Избегать иерархических «замков», если можно

  29. Поток A завладел L1 и удерживает его в надежде дождаться L2; но это никогда не наступит, так как L2 уже захватил поток B и удерживает в надежде захватить L1 – это тоже никогда не наступит Тупики или бесконечные зависания – пример • DWORD WINAPI threadA(LPVOID arg) • { • EnterCriticalSection(&L1); • EnterCriticalSection(&L2); • processA(data1, data2); • LeaveCriticalSection(&L2); • LeaveCriticalSection(&L1); • return(0); • } Поток B: L2, затем L1 DWORD WINAPI threadB(LPVOID arg) { EnterCriticalSection(&L2); EnterCriticalSection(&L1); processB(data1, data2) ; LeaveCriticalSection(&L1); LeaveCriticalSection(&L2); return(0); } Поток A: L1, затем L2

  30. Thread 4 swap(Q[986], Q[34]); Thread 1 Захват Q[34] Захват Q[986] swap(Q[34], Q[986]); Тупики (Deadlock) - пример • Правило: блокировка только одного элемента массива • Нарушение правила: функция устанавливает блокировку на две переменные (при вызове – на два элемента массива). Поток 1 захватил Q[34]и ждет Q[986], поток 4 – захватил Q[986] и ждет Q[34] – возник «тупик» typedef struct { // some data things SomeLockType mutex; } shape_t; shape_t Q[1024]; void swap (shape_t A, shape_t B) { lock(a.mutex); lock(b.mutex); // Swap data between A & B unlock(b.mutex); unlock(a.mutex); }

  31. Поток завис или «застрял»...(Thread Stalls) • Поток ожидает неразумное количество времени – слишком долго • Обычно он ждет ресурс • Как правило, бывают вызваны «зависшими блокировками» Проверьте, что потоки освободили все наложенные блокировки

  32. «Замок» никогда не будет снят Вход и выход в критическую секцию должны быть «парной операцией». А если data == DONE_FLAG, происходит возврат без освобождения критической секции Что здесь неверно? • intdata; • DWORD WINAPI threadFunc(LPVOID arg) • { • int localData; • EnterCriticalSection(&lock); • if (data == DONE_FLAG) • return(1); • localData = data; • LeaveCriticalSection(&lock); • process(local_data); • return(0); • }

  33. Задание 3 – тупик (deadlock – «замок» «намертво», «мертвый lock») • В задаче поиска простых чисел установите комментарий на операцию освобождения критической секции • Проверьте реакцию Intel® Thread Checkerнавозникший тупик

  34. «Поточно-безопасные» функции • Все функции, одновременно вызываемые несколькими потоками, должны быть «поточно-безопасными» • Как протестировать на «безопасность» многопоточного выполнения? • С помощью OpenMP и Thread Checker • Организовать многопоточность с помощью OpenMP • Примените конструкцию OpenMP «параллельные секции», чтобы организовать многопоточное выполнение

  35. Здесь тестируется безопасность многопоточного выполнения для Одновременной работы нескольких вариантов routine1() Одновременного выполнения routine1()иroutine2() Конструкция OPenMP «параллельные секции» - для тестирования всех комбинаций Необходимо только позаботиться о соответствующих наборах данных для каждого участка кода Пример тестирования «безопасности» многопоточного выполнения (Thread Safety) • #pragma omp parallel sections { #pragma omp section routine1(&data1); #pragma omp section routine1(&data2); #pragma omp section routine2(&data3); }

  36. Лучше сделать функцию используемой повторно, чем добавить синхронизацию • Избежите возможного оверхеда Два способа обеспечить безопасность многопоточного выполнения • Сделать функцию повторно используемой • Любые переменные, изменяемые функцией, должны быть локальными для каждого вызова • Не изменять разделяемых переменных • Функции могут использовать взаимное исключение, чтобы избежать конфликтов с другими потоками • Если только разделяемого доступа совсем невозможно избежать • А если функции из третьей секции не поточно-безопасны? • Скорее всего, придется управлять доступом потоков к библиотеке

  37. Задание 4– тестирование на «безопасность» многопоточного выполнения (Thread Safety) • Используйте OpenMP, чтобы организовать одновременное выполнение функций • Вызов трех библиотечных функций= 6 комбинаций для тестирования • A:A, B:B, C:C, A:B, A:C, B:C

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

  39. Огромное количество диагностик • Что же делать, если у вас 5000 диагностик? • В каком месте начинать отладку? • Все ли сообщения одинаково важны? • Как систематизировать и определить приоритеты • Добавьте столбец “1st Access” (первый доступ) • Создайте группу “1st Access” • Создайте группы по “Short Description” (короткое описание)

  40. Много диагностик... Add the “1st Access” column if it not already present

  41. Много диагностик...

  42. Создайте группы ошибок, вызванных одной и той же строкой кода; каждая из групп может выглядеть так Много диагностик...

  43. Сортируйте по “Short description” Много диагностик...

  44. Intel® Thread CheckerЧто он может... • Легко выявляет ошибки многопоточного приложения, которые трудно выявить обычным способом • Intel® Thread Checker находит • Ошибки, которые необязательно должны произойти, чтобы быть выловленными • Резко сокращает время отладки • Повышает надежность приложения

More Related