SlideShare a Scribd company logo
Отладка и оптимизация
многопоточных OpenMP-программ
Авторы: Андрей Карпов, Евгений Романовский

Дата: 24.01.2009


Аннотация
Задача знакомства программистов с областью разработки параллельных приложений становится
все актуальней. Данная статья является кратким введением в создание многопоточных
приложений, основанных на технологии OpenMP. Описаны подходы к отладке и оптимизации
параллельных приложений.

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

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

Отладка последовательной программы основана на том, что степень предсказуемости начального
и текущего состояний программы определяется входными данными. Когда программист
переходит к отладке многопоточного кода, то он обычно сталкивается с совершенно уникальными
проблемами: в различных операционных системах применяются разные стратегии планирования,
нагрузка на вычислительную систему динамически изменяется, приоритеты процессов могут
различаться и т. д. Точное воссоздание состояния программы в некоторый момент ее выполнения
(тривиальная задача для последовательной отладки) значительно усложняется при переходе к
параллельной программе, что связано с недетерминированным поведением последней. Иными
словами, поведение запущенных в системе процессов, а именно их выполнение и ожидание
выполнения, взаимные блокировки и прочее, зависит от случайных событий, происходящих в
системе. Как же быть? Очевидно, для отладки параллельного кода требуются совершенно другие
средства.

По мере того, как параллельные вычислительные системы стали обычным явлением в
потребительском сегменте рынка, спрос на средства отладки многопоточных приложений
существенно увеличился. Мы рассмотрим отладку и повышение производительности
многопоточного приложения, построенного на основе технологии OpenMP. Полный текст
программы, из которого мы будем приводить отдельные участки кода содержится в конце статьи
в приложении N1.
Для примера возьмем последовательный программный код функции Function, приведенный в
листинге 1. Эта простая подпрограмма вычисляет значения некоторой математической функции,
имеющей один аргумент.

double Function(int N)

{

    double x, y, s=0;

    for (int i=1; i<=N; i++) {

     x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         s += j * y;

         y = y * x;

     };

    };

    return s;

}

При вызове этой функции с аргументом N, равным 15000, мы получим резуьтат 287305025.528.

Эту функцию можно легко распараллелить с помощью средств стандарта OpenMP. Добавим одну
единственную строку перед первым оператором for (листинг 2).

double FunctionOpenMP(int N)

{

    double x, y, s=0;

    #pragma omp parallel for num_threads(2)

    for (int i=1; i<=N; i++) {

     x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         s += j * y;

         y = y * x;

     };

    };
return s;

}

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

Основная причина ошибок в параллельных программах — некорректная работа с разделяемыми,
т. е. общими для всех запущенных процессов ресурсами, в частном случае — с общими
переменными.

Данная программа успешно компилируется в среде Microsoft Visual Studio 2005, причем
компилятор даже не выдает никаких предупреждений. Однако она некорректна. Чтобы это
понять, надо вспомнить, что в OpenMP-программах переменные делятся на общие (shared),
существующие в одном экземпляре и доступные всем потокам, и частные (private),
локализованные в конкретном процессе. Кроме того, есть правило, гласящее, что по умолчанию
все переменные в параллельных регионах OpenMP общие, за исключением индексов
параллельных циклов и переменных, объявленных внутри этих параллельных регионов.

В приведенном выше примере видно, что переменные x, y и s — общие, что совершенно
неправильно. Переменная s обязательно должна быть общей, так как в рассматриваемом
алгоритме она является, по сути, сумматором. Однако при работе с переменными x или y каждый
процесс вычисляет очередное их значение и записывает в соответствующую переменную (x или
y). И тогда результат вычислений зависит от того, в какой последовательности выполнялись
параллельные потоки. Иначе говоря, если первый поток вычислит значение для x, запишет его в
переменную x, а потом такие же действия произведет второй поток, то при попытке прочитать
значение переменной x первым потоком он получит то значение, которые было записано туда
последним по времени, а значит, вычисленное вторым потоком. Подобные ошибки в случае,
когда работа программы зависит от порядка выполнения различных фрагментов кода,
называются race condition или data race (состояние "гонки" или "гонки" вычислительных потоков;
подразумевается, что имеют место несинхронизированные обращения к памяти).

Для поиска таких ошибок необходимы специальные программные средства. Одно из них - Intel
Thread Checker. Адрес продукта: http://www.viva64.com/go.php?url=526. Данная программа
поставляется как модуль к профилировщику Intel VTune Performance Analyzer, дополняя
имеющиеся средства для работы с многопоточным кодом. Intel Thread Checker позволяет
обнаружить как описанные выше ошибки, так и многие другие, например deadlocks ("тупики",
места взаимной блокировки вычислительных нитей) и утечки памяти.

После установки Intel Thread Checker в диалоге New Project приложения Intel VTune Performance
Analyzer появится новая категория проектов — Threading Wizards (мастера для работы с потоками),
среди которых будет Intel Thread Checker Wizard. Для запуска примера необходимо выбрать его, а
в следующем окне мастера указать путь к запускаемой программе. После запуска программа
начнет выполняться, а профилировщик соберет все сведения о работе приложения. Пример такой
информации, выдаваемой Intel Thread Checker, приведен на рисунке 1.
Рисунок 1 - В результате работы Thread Checker обнаружено множество критических ошибок

Как видно, даже для такой небольшой программы количество ошибок достаточно велико. Thread
Checker группирует обнаруженные ошибки, одновременно оценивая их критичность для работы
программы, и приводит их описание, что существенно повышает эффективность работы
программиста. Кроме того, на вкладке Source View представлен программный код приложения с
указанием тех мест в коде, где имеются ошибки (рисунок 2).
Рисунок 2 - Анализ многопоточного кода Intel Thread Checker

Следует учитывать, что Intel Thread Checker в ряде случаях не может выявить ошибку. Это
относится к коду, который редко получает управление или выполняется на системе с другой
архитектурой. Ошибка также может быть пропущена, когда набор входных тестовых данных
сильно отличается от данных обрабатываемых программой при ее эксплуатации конечными
пользователями. Все это не позволяет быть уверенным в отсутствии ошибок в многопоточной
программе, после проверки ее с использованием динамических средств анализа, результат
которых зависит от среды и времени исполнения.
Но хорошей новостью для разработчиков OpenMP является существование и другого инструмента
- VivaMP, предлагающего альтернативный подход к верификации параллельных программ.
VivaMP построен по принципу статического анализатора кода и позволяет проверять код
приложения без его запуска. Более подробно с инструментом VivaMP можно познакомиться на
сайте разработчиков http://www.viva64.com/ru/vivamp-tool/.

Области применения VivaMP:

   •   Контроль корректности кода разрабатываемых приложений на базе технологии OpenMP.
   •   Помощь в освоении технологии OpenMP и интеграция ее в уже существующие проекты.
   •   Создание более эффективных в использовании ресурсов параллельных приложений.
   •   Поиск ошибок в существующих OpenMP приложениях.

Анализатор VivaMP интегрируется в среду Visual Studio 2005/2008 и предоставляет простой
интерфейс для проверки приложений (рисунок 3).
Рисунок 3 - Запуск инструмента VivaMP, интегрированного в Visual Studio 2005
                                       VivaMP,

Если мы запустим VivaMP для нашего примера, то получим сообщение об ошибках в 4 различных
строках, где происходит некорректная модификация переменных (рисунок 4).




              Рисунок 4 - Результат работы статического анализатора VivaMP
                                                        анализатора

Конечно, статический анализ также имеет ряд недостатков, как и динамический анализ. Но вместе
эти две методологии (два инструмента Intel Thread Checker и VivaMP) отлично дополнят друг
друга. И их совместное использование является достаточно надежным методом выявления
                                                          надежным
ошибок в многопоточных приложениях.

Описанную выше и обнаруженную средствами Intel Thread Checker и VivaMP ошибку записи в
переменные x и y исправить довольно просто: нужно лишь добавить в конструкцию #pragma omp
parallel for еще одну директиву private (x, y). Таким образом, эти две переменные будут
                      директиву:
объявлены как частные, и в каждом вычислительном потоке будут свои копии x и y. Следует также
обратить внимание, что все потоки сохраняют вычисленный результат добавлением его к
переменной s. Подобные ошибки происходят тогда, когда один вычислительный поток пытается
         нной
записать некоторое значение в общую память, а другой в то же время выполняет операцию
чтения. В рассматриваемом примере это может привести к некорректному результату.

Рассмотрим инструкцию s += j*y. Изначально предполагается, что каждый поток суммирует
      трим
вычисленный результат с текущим значением переменной s, а потом такие же действия
выполняют остальные потоки. Однако возможна ситуация, когда, например, два потока
одновременно начали выполнять инструкцию s += j*y, т. е. каждый из них сначала прочитает
текущее значение переменной s, затем прибавит к этому значению результат умножения j*y и
полученное запишет в общую переменную s.

В отличие от операции чтения, которая может быть реализована параллельно и является
достаточно быстрой, операция записи всегда последовательна. Следовательно, если сначала
первый поток записал новое значение, то второй поток, выполнив после этого запись, затрет
результат вычислений первого, потому что оба вычислительных потока сначала прочитали одно и
то же значение s, а потом стали записывать свои данные в эту переменную. Иными словами, то
значение s, которое второй поток в итоге запишет в общую память, никак не учитывает результат
вычислений, полученный в первом потоке. Можно избежать подобной ситуации, если
гарантировать, что в любой момент времени операцию s += j*y разрешается выполнять только
одному из потоков. Такие операции называются неделимыми или атомарными. Когда нужно
указать компилятору, что какая-либо инструкция является атомарной, используется конструкция
#pragma omp atomic. Программный код, в котором исправлены указанные ошибки, приведен в
листинге 3.

double FixedFunctionOpenMP(int N)

{

    double x, y, s=0;

    #pragma omp parallel for 

             private(x,y) num_threads(2)

    for (int i=1; i<=N; i++) {

     x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         #pragma omp atomic

         s += j * y;

         y = y * x;

     };

    };

    return s;

}

После перекомпиляции программы и ее повторного анализа в Thread Checker мы увидим, что
программа не содержит критических ошибок. Выводятся только два информационных сообщения
о том, что параллельные потоки завершаются при достижении оператора return в функции
MathFunction. В рассматриваемом примере так и должно быть, потому что распараллеливается
только код внутри данной функции. Статический анализатор VivaMP не выдаст на этот код вообще
никаких диагностических сообщений, так как он полностью корректен с его точки зрения.

Но отдыхать еще рано. Давайте уточним, действительно ли наш код стал более эффективным
после распараллеливания. Замерим время выполнения трех функций: 1 - последовательной, 2 -
параллельной некорректной, 3 - параллельной корректной. Результаты такого измерения для
N=15000 приведены в таблице 1.
Функция                                                       Результат     Время
                                                                            выполнения
Последовательный вариант функции                              287305025.528 0.5781 секунд
Некорректный вариант параллельной функции                     298441282.231 2.9531 секунд
Корректный вариант параллельной функции, использующий         287305025.528 36.8281 секунд
директиву atomic
Таблица 1 - Результат работы функций

И что мы видим в таблице? А то, что параллельный вариант некорректной функции работает в
несколько раз медленнее. Но нас эта функция не интересует. Беда в том, что правильный вариант
работает вообще более чем в 60 раз медленнее. Нам нужна такая параллельность? Конечно, нет.

Все дело в том, что мы выбрали крайне неэффективный метод решения проблемы с
суммированием результата в переменной s, использованием директивы atomic. Такой подход
приводит к частому ожиданию потоками друг друга. Чтобы избежать постоянных взаимных
блокировок при выполнении атомарной операции суммирования мы можем использовать
специальную директиву reduction. Опция reduction определяет, что на выходе из параллельного
блока переменная получит комбинированное значение. Допустимы следующие операции: +, *, -,
&, |, ^, &&, ||. Модифицированный вариант функции показан в листинге 4.

double OptimizedFunction(int N)

{

    double x, y, s=0;

    #pragma omp parallel for private(x,y) 

             num_threads(2) reduction(+: s)

    for (int i=1; i<=N; i++) {

     x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         s += j * y;

         y = y * x;

     };

    };

    return s;

}

На этот мы получим не только корректный, но и более производительный вариант функции
(таблица 2). Скорость вычисления возросла почти в 2 раза (в 1.85 раз), что является очень
хорошим показателем для подобных функций.

Функция                                                       Результат        Время
выполнения
Последовательный вариант функции                           287305025.528 0.5781 секунд
Некорректный вариант параллельной функции                  298441282.231 2.9531 секунд
Корректный вариант параллельной функции, использующий      287305025.528 36.8281 секунд
директиву atomic
Корректный вариант параллельной функции, использующий      287305025.528 0.3125 секунд
директиву reduction
Таблица 2 - Результат работы функций

В заключение еще раз хочется подчеркнуть, что работоспособная параллельная программа может
далеко не всегда являться эффективной. И хотя параллельное программирование предоставляет
множество способов повышения эффективности кода, оно требует от программиста
внимательности и хороших знаний используемых им технологий. К счастью существуют такие
инструменты, как Intel Thread Checker и VivaMP, существенно облегчающих создание и проверки
многопоточных приложений. Удачи вам уважаемые читатели в освоении новой области знаний.


Приложение N1. Текст демонстрационной программы
#include "stdafx.h"

#include <omp.h>

#include <stdlib.h>

#include <windows.h>

class VivaMeteringTimeStruct {

public:

    VivaMeteringTimeStruct()

     { m_userTime = GetCurrentUserTime(); }

    ~VivaMeteringTimeStruct()

     { printf("Time = %.4f secondsn", GetUserSeconds()); }

    double GetUserSeconds();

private:

    __int64 GetCurrentUserTime() const;

    __int64 m_userTime;

};

__int64 VivaMeteringTimeStruct::GetCurrentUserTime() const

{

    FILETIME creationTime, exitTime, kernelTime, userTime;

    GetThreadTimes(GetCurrentThread(), &creationTime,
&exitTime, &kernelTime, &userTime);

    __int64 curTime;

    curTime = userTime.dwHighDateTime;

    curTime <<= 32;

    curTime += userTime.dwLowDateTime;

    return curTime;

}

double VivaMeteringTimeStruct::GetUserSeconds()

{

    __int64 delta = GetCurrentUserTime() - m_userTime;

    return double(delta) / 10000000.0;

}

double Function(int N)

{

    double x, y, s=0;

    for (int i=1; i<=N; i++) {

     x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         s += j * y;

         y = y * x;

     };

    };

    return s;

}

double FunctionOpenMP(int N)

{

    double x, y, s=0;

    #pragma omp parallel for num_threads(2)

    for (int i=1; i<=N; i++) {
x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         s += j * y;

         y = y * x;

     };

    };

    return s;

}

double FixedFunctionOpenMP(int N)

{

    double x, y, s=0;

    #pragma omp parallel for private(x,y) num_threads(2)

    for (int i=1; i<=N; i++) {

     x = (double)i/N;

     y = x;

     for (int j=1; j<=N; j++) {

         #pragma omp atomic

         s += j * y;

         y = y * x;

     };

    };

    return s;

}

double OptimizedFunction(int N)

{

    double x, y, s=0;

    #pragma omp parallel for private(x,y) 

            num_threads(2) reduction(+: s)

    for (int i=1; i<=N; i++) {
x = (double)i/N;

        y = x;

        for (int j=1; j<=N; j++) {

         s += j * y;

         y = y * x;

        };

    };

    return s;

}

int _tmain(int , _TCHAR* [])

{

    int N = 15000;

    {

        VivaMeteringTimeStruct Timer;

        printf("Result = %.3f   ", Function(N));

    }

    {

        VivaMeteringTimeStruct Timer;

        printf("Result = %.3f   ", FunctionOpenMP(N));

    }

    {

        VivaMeteringTimeStruct Timer;

        printf("Result = %.3f   ", FixedFunctionOpenMP(N));

    }

    {

        VivaMeteringTimeStruct Timer;

        printf("Result = %.3f   ", OptimizedFunction(N));

    }

    return 0;

}

More Related Content

What's hot

Технология OpenMP
Технология OpenMPТехнология OpenMP
Технология OpenMP
Tatyanazaxarova
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
Tatyanazaxarova
 
Регулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработкеРегулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработке
Tatyanazaxarova
 
лекция 3
лекция 3лекция 3
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6Technopark
 
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Andrey Karpov
 
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Tatyanazaxarova
 
Java 9 - Back to the Future
Java 9 - Back to the FutureJava 9 - Back to the Future
Java 9 - Back to the Future
Леонид Ставила
 
Андрей Кирпичев "Гибкая модульность инструментами АОП"
Андрей Кирпичев  "Гибкая модульность инструментами АОП"Андрей Кирпичев  "Гибкая модульность инструментами АОП"
Андрей Кирпичев "Гибкая модульность инструментами АОП"
MskDotNet Community
 
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
yaevents
 
СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...
СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...
СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...
ITMO University
 
Специфика разработки и тестирования статического анализатора
Специфика разработки и тестирования статического анализатораСпецифика разработки и тестирования статического анализатора
Специфика разработки и тестирования статического анализатора
Andrey Karpov
 
20120218 model checking_karpov_lecture02
20120218 model checking_karpov_lecture0220120218 model checking_karpov_lecture02
20120218 model checking_karpov_lecture02Computer Science Club
 
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Tatyanazaxarova
 
Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...
Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...
Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...
corehard_by
 

What's hot (20)

Технология OpenMP
Технология OpenMPТехнология OpenMP
Технология OpenMP
 
Урок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибокУрок 7. Проблемы выявления 64-битных ошибок
Урок 7. Проблемы выявления 64-битных ошибок
 
Регулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработкеРегулярное использование статического анализа кода в командной разработке
Регулярное использование статического анализа кода в командной разработке
 
лекция 3
лекция 3лекция 3
лекция 3
 
C++ осень 2012 лекция 6
C++ осень 2012 лекция 6C++ осень 2012 лекция 6
C++ осень 2012 лекция 6
 
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++Реклама PVS-Studio - статический анализ кода на языке Си и Си++
Реклама PVS-Studio - статический анализ кода на языке Си и Си++
 
Багдатов Методы автоматического выявления плагиата в текстах компьютерных про...
Багдатов Методы автоматического выявления плагиата в текстах компьютерных про...Багдатов Методы автоматического выявления плагиата в текстах компьютерных про...
Багдатов Методы автоматического выявления плагиата в текстах компьютерных про...
 
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
Интервью с Дмитрием Вьюковым – автором верификатора Relacy Race Detector (RRD)
 
Java 9 - Back to the Future
Java 9 - Back to the FutureJava 9 - Back to the Future
Java 9 - Back to the Future
 
Андрей Кирпичев "Гибкая модульность инструментами АОП"
Андрей Кирпичев  "Гибкая модульность инструментами АОП"Андрей Кирпичев  "Гибкая модульность инструментами АОП"
Андрей Кирпичев "Гибкая модульность инструментами АОП"
 
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
 
лек11 4
лек11 4лек11 4
лек11 4
 
лек5 6
лек5 6лек5 6
лек5 6
 
СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...
СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...
СОВМЕСТНОЕ ПРИМЕНЕНИЕ КОНТРАКТОВ И ВЕРИФИКАЦИИ ДЛЯ ПОВЫШЕНИЯ КАЧЕСТВА АВТОМАТ...
 
Специфика разработки и тестирования статического анализатора
Специфика разработки и тестирования статического анализатораСпецифика разработки и тестирования статического анализатора
Специфика разработки и тестирования статического анализатора
 
20120218 model checking_karpov_lecture02
20120218 model checking_karpov_lecture0220120218 model checking_karpov_lecture02
20120218 model checking_karpov_lecture02
 
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
Использование анализатора PVS-Studio в процессе инкрементальной сборки в Micr...
 
Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...
Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...
Этюды о буферизации: асинхронные оповещения, репликация обновлений, объединен...
 
Step 1
Step 1Step 1
Step 1
 
Step 5
Step 5Step 5
Step 5
 

Viewers also liked

Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структур
Tatyanazaxarova
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данных
Tatyanazaxarova
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Tatyanazaxarova
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибки
Tatyanazaxarova
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программ
Tatyanazaxarova
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. Исключения
Tatyanazaxarova
 
Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окружения
Tatyanazaxarova
 

Viewers also liked (7)

Урок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структурУрок 23. Паттерн 15. Рост размеров структур
Урок 23. Паттерн 15. Рост размеров структур
 
Урок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данныхУрок 21. Паттерн 13. Выравнивание данных
Урок 21. Паттерн 13. Выравнивание данных
 
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибокУрок 25. Практическое знакомство с паттернами 64-битных ошибок
Урок 25. Практическое знакомство с паттернами 64-битных ошибок
 
Урок 24. Фантомные ошибки
Урок 24. Фантомные ошибкиУрок 24. Фантомные ошибки
Урок 24. Фантомные ошибки
 
Урок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программУрок 26. Оптимизация 64-битных программ
Урок 26. Оптимизация 64-битных программ
 
Урок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. ИсключенияУрок 20. Паттерн 12. Исключения
Урок 20. Паттерн 12. Исключения
 
Урок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окруженияУрок 27. Особенности создания инсталляторов для 64-битного окружения
Урок 27. Особенности создания инсталляторов для 64-битного окружения
 

Similar to Отладка и оптимизация многопоточных OpenMP-программ

VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...
VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...
VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...
Tatyanazaxarova
 
Метрики кода программного обеспечения
Метрики кода программного обеспеченияМетрики кода программного обеспечения
Метрики кода программного обеспечения
Tatyanazaxarova
 
Построение систем автоматического протоколирования Си/Си++ кода
Построение систем автоматического протоколирования Си/Си++ кодаПостроение систем автоматического протоколирования Си/Си++ кода
Построение систем автоматического протоколирования Си/Си++ кода
Tatyanazaxarova
 
Как мы тестируем анализатор кода
Как мы тестируем анализатор кодаКак мы тестируем анализатор кода
Как мы тестируем анализатор кода
Tatyanazaxarova
 
Net framework
Net frameworkNet framework
Net framework
poverhnost
 
Mva stf module 1 - rus
Mva stf module 1 - rusMva stf module 1 - rus
Mva stf module 1 - rus
Maxim Shaptala
 
Статический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложенийСтатический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложений
Tatyanazaxarova
 
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Tatyanazaxarova
 
оп.05 основы программирования
оп.05 основы программированияоп.05 основы программирования
оп.05 основы программирования
Stepan1234
 
основы ооп на языке C#. часть 1. введение в программирование
основы ооп на языке C#. часть 1. введение в программированиеосновы ооп на языке C#. часть 1. введение в программирование
основы ооп на языке C#. часть 1. введение в программирование
YakubovichDA
 
Apache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектовApache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектов
SQALab
 
Облегчаем процесс разработки с помощью статического анализа кода: Наш опыт
Облегчаем процесс разработки с помощью статического анализа кода: Наш опытОблегчаем процесс разработки с помощью статического анализа кода: Наш опыт
Облегчаем процесс разработки с помощью статического анализа кода: Наш опыт
Andrey Karpov
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1Michael Karpov
 
Липский Павел
Липский ПавелЛипский Павел
Липский ПавелOntico
 
Presentation for students about Laravel.pptx
Presentation for students about Laravel.pptxPresentation for students about Laravel.pptx
Presentation for students about Laravel.pptx
GetOverHere
 
Разница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомРазница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментом
Tatyanazaxarova
 
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art) DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art) it-people
 
UAFPUG6 - PureMVC
UAFPUG6 - PureMVCUAFPUG6 - PureMVC
UAFPUG6 - PureMVCmandrew182
 
PureMVC and Papervision
PureMVC and PapervisionPureMVC and Papervision
PureMVC and Papervision
Dmitry Kuriksha
 

Similar to Отладка и оптимизация многопоточных OpenMP-программ (20)

VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...
VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...
VivaMP, система выявления ошибок в коде параллельных программ на языке С++, и...
 
Метрики кода программного обеспечения
Метрики кода программного обеспеченияМетрики кода программного обеспечения
Метрики кода программного обеспечения
 
Построение систем автоматического протоколирования Си/Си++ кода
Построение систем автоматического протоколирования Си/Си++ кодаПостроение систем автоматического протоколирования Си/Си++ кода
Построение систем автоматического протоколирования Си/Си++ кода
 
Как мы тестируем анализатор кода
Как мы тестируем анализатор кодаКак мы тестируем анализатор кода
Как мы тестируем анализатор кода
 
Net framework
Net frameworkNet framework
Net framework
 
Sonar quality
Sonar qualitySonar quality
Sonar quality
 
Mva stf module 1 - rus
Mva stf module 1 - rusMva stf module 1 - rus
Mva stf module 1 - rus
 
Статический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложенийСтатический анализ кода для верификации 64-битных приложений
Статический анализ кода для верификации 64-битных приложений
 
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
Поиск ловушек в Си/Си++ коде при переносе приложений под 64-битную версию Win...
 
оп.05 основы программирования
оп.05 основы программированияоп.05 основы программирования
оп.05 основы программирования
 
основы ооп на языке C#. часть 1. введение в программирование
основы ооп на языке C#. часть 1. введение в программированиеосновы ооп на языке C#. часть 1. введение в программирование
основы ооп на языке C#. часть 1. введение в программирование
 
Apache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектовApache.JMeter для .NET-проектов
Apache.JMeter для .NET-проектов
 
Облегчаем процесс разработки с помощью статического анализа кода: Наш опыт
Облегчаем процесс разработки с помощью статического анализа кода: Наш опытОблегчаем процесс разработки с помощью статического анализа кода: Наш опыт
Облегчаем процесс разработки с помощью статического анализа кода: Наш опыт
 
20090720 hpc exercise1
20090720 hpc exercise120090720 hpc exercise1
20090720 hpc exercise1
 
Липский Павел
Липский ПавелЛипский Павел
Липский Павел
 
Presentation for students about Laravel.pptx
Presentation for students about Laravel.pptxPresentation for students about Laravel.pptx
Presentation for students about Laravel.pptx
 
Разница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментомРазница в подходах анализа кода компилятором и выделенным инструментом
Разница в подходах анализа кода компилятором и выделенным инструментом
 
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art) DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
DUMP-2012 - Только хардкор! - "Расширяем PHP" Сергей Горшков (index.art)
 
UAFPUG6 - PureMVC
UAFPUG6 - PureMVCUAFPUG6 - PureMVC
UAFPUG6 - PureMVC
 
PureMVC and Papervision
PureMVC and PapervisionPureMVC and Papervision
PureMVC and Papervision
 

More from Tatyanazaxarova

Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен данными
Tatyanazaxarova
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
Tatyanazaxarova
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединениях
Tatyanazaxarova
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателей
Tatyanazaxarova
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
Tatyanazaxarova
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвига
Tatyanazaxarova
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Tatyanazaxarova
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числа
Tatyanazaxarova
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибок
Tatyanazaxarova
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном коде
Tatyanazaxarova
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложения
Tatyanazaxarova
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурации
Tatyanazaxarova
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложений
Tatyanazaxarova
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кода
Tatyanazaxarova
 
PVS-Studio
PVS-Studio PVS-Studio
PVS-Studio
Tatyanazaxarova
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируете
Tatyanazaxarova
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-Paste
Tatyanazaxarova
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROI
Tatyanazaxarova
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времени
Tatyanazaxarova
 
По колено в Си++ г... коде
По колено в Си++ г... кодеПо колено в Си++ г... коде
По колено в Си++ г... коде
Tatyanazaxarova
 

More from Tatyanazaxarova (20)

Урок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен даннымиУрок 19. Паттерн 11. Сериализация и обмен данными
Урок 19. Паттерн 11. Сериализация и обмен данными
 
Урок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметикаУрок 17. Паттерн 9. Смешанная арифметика
Урок 17. Паттерн 9. Смешанная арифметика
 
Урок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединенияхУрок 16. Паттерн 8. Memsize-типы в объединениях
Урок 16. Паттерн 8. Memsize-типы в объединениях
 
Урок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателейУрок 15. Паттерн 7. Упаковка указателей
Урок 15. Паттерн 7. Упаковка указателей
 
Урок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметикаУрок 13. Паттерн 5. Адресная арифметика
Урок 13. Паттерн 5. Адресная арифметика
 
Урок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвигаУрок 11. Паттерн 3. Операции сдвига
Урок 11. Паттерн 3. Операции сдвига
 
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументовУрок 10. Паттерн 2. Функции с переменным количеством аргументов
Урок 10. Паттерн 2. Функции с переменным количеством аргументов
 
Урок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числаУрок 9. Паттерн 1. Магические числа
Урок 9. Паттерн 1. Магические числа
 
Урок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибокУрок 8. Статический анализ для выявления 64-битных ошибок
Урок 8. Статический анализ для выявления 64-битных ошибок
 
Урок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном кодеУрок 6. Ошибки в 64-битном коде
Урок 6. Ошибки в 64-битном коде
 
Урок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложенияУрок 5. Сборка 64-битного приложения
Урок 5. Сборка 64-битного приложения
 
Урок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурацииУрок 4. Создание 64-битной конфигурации
Урок 4. Создание 64-битной конфигурации
 
PVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложенийPVS-Studio, решение для разработки современных ресурсоемких приложений
PVS-Studio, решение для разработки современных ресурсоемких приложений
 
Статический анализ Си++ кода
Статический анализ Си++ кодаСтатический анализ Си++ кода
Статический анализ Си++ кода
 
PVS-Studio
PVS-Studio PVS-Studio
PVS-Studio
 
PVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируетеPVS-Studio научился следить за тем, как вы программируете
PVS-Studio научился следить за тем, как вы программируете
 
Пояснения к статье про Copy-Paste
Пояснения к статье про Copy-PasteПояснения к статье про Copy-Paste
Пояснения к статье про Copy-Paste
 
Статический анализ и ROI
Статический анализ и ROIСтатический анализ и ROI
Статический анализ и ROI
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времени
 
По колено в Си++ г... коде
По колено в Си++ г... кодеПо колено в Си++ г... коде
По колено в Си++ г... коде
 

Отладка и оптимизация многопоточных OpenMP-программ

  • 1. Отладка и оптимизация многопоточных OpenMP-программ Авторы: Андрей Карпов, Евгений Романовский Дата: 24.01.2009 Аннотация Задача знакомства программистов с областью разработки параллельных приложений становится все актуальней. Данная статья является кратким введением в создание многопоточных приложений, основанных на технологии OpenMP. Описаны подходы к отладке и оптимизации параллельных приложений. В предложенной статье рассматривается технология OpenMP, главная ценность которой в возможности доработки и оптимизации уже созданного последовательного кода. Стандарт OpenMP предоставляет собой набор спецификаций для распараллеливания кода в среде с общей памятью. Ключевое условие для использования OpenMP - поддержка этого стандарта со стороны компилятора. Кроме того, требуются принципиально новые инструменты для этапа отладки, на котором обнаруживаются, локализуются и устраняются ошибки, и производится оптимизация. Отладчик для последовательного кода это хорошо знакомый и активно используемый программистом инструмент. Он предоставляет разработчику возможность отслеживать изменения значений переменных при пошаговом выполнении программы с помощью развитого пользовательского интерфейса. Однако ситуация значительно меняется, когда заходит речь об отладке и тестировании многопоточных приложений. А именно создание многопоточных приложений становится основным направлением в создании эффективных приложений. Отладка последовательной программы основана на том, что степень предсказуемости начального и текущего состояний программы определяется входными данными. Когда программист переходит к отладке многопоточного кода, то он обычно сталкивается с совершенно уникальными проблемами: в различных операционных системах применяются разные стратегии планирования, нагрузка на вычислительную систему динамически изменяется, приоритеты процессов могут различаться и т. д. Точное воссоздание состояния программы в некоторый момент ее выполнения (тривиальная задача для последовательной отладки) значительно усложняется при переходе к параллельной программе, что связано с недетерминированным поведением последней. Иными словами, поведение запущенных в системе процессов, а именно их выполнение и ожидание выполнения, взаимные блокировки и прочее, зависит от случайных событий, происходящих в системе. Как же быть? Очевидно, для отладки параллельного кода требуются совершенно другие средства. По мере того, как параллельные вычислительные системы стали обычным явлением в потребительском сегменте рынка, спрос на средства отладки многопоточных приложений существенно увеличился. Мы рассмотрим отладку и повышение производительности многопоточного приложения, построенного на основе технологии OpenMP. Полный текст программы, из которого мы будем приводить отдельные участки кода содержится в конце статьи в приложении N1.
  • 2. Для примера возьмем последовательный программный код функции Function, приведенный в листинге 1. Эта простая подпрограмма вычисляет значения некоторой математической функции, имеющей один аргумент. double Function(int N) { double x, y, s=0; for (int i=1; i<=N; i++) { x = (double)i/N; y = x; for (int j=1; j<=N; j++) { s += j * y; y = y * x; }; }; return s; } При вызове этой функции с аргументом N, равным 15000, мы получим резуьтат 287305025.528. Эту функцию можно легко распараллелить с помощью средств стандарта OpenMP. Добавим одну единственную строку перед первым оператором for (листинг 2). double FunctionOpenMP(int N) { double x, y, s=0; #pragma omp parallel for num_threads(2) for (int i=1; i<=N; i++) { x = (double)i/N; y = x; for (int j=1; j<=N; j++) { s += j * y; y = y * x; }; };
  • 3. return s; } К сожалению, созданный нами код является некорректным и результат работы функции в общем случае не определен. Например, он может быть равен 298441282.231. Попробуем разобраться в причинах. Основная причина ошибок в параллельных программах — некорректная работа с разделяемыми, т. е. общими для всех запущенных процессов ресурсами, в частном случае — с общими переменными. Данная программа успешно компилируется в среде Microsoft Visual Studio 2005, причем компилятор даже не выдает никаких предупреждений. Однако она некорректна. Чтобы это понять, надо вспомнить, что в OpenMP-программах переменные делятся на общие (shared), существующие в одном экземпляре и доступные всем потокам, и частные (private), локализованные в конкретном процессе. Кроме того, есть правило, гласящее, что по умолчанию все переменные в параллельных регионах OpenMP общие, за исключением индексов параллельных циклов и переменных, объявленных внутри этих параллельных регионов. В приведенном выше примере видно, что переменные x, y и s — общие, что совершенно неправильно. Переменная s обязательно должна быть общей, так как в рассматриваемом алгоритме она является, по сути, сумматором. Однако при работе с переменными x или y каждый процесс вычисляет очередное их значение и записывает в соответствующую переменную (x или y). И тогда результат вычислений зависит от того, в какой последовательности выполнялись параллельные потоки. Иначе говоря, если первый поток вычислит значение для x, запишет его в переменную x, а потом такие же действия произведет второй поток, то при попытке прочитать значение переменной x первым потоком он получит то значение, которые было записано туда последним по времени, а значит, вычисленное вторым потоком. Подобные ошибки в случае, когда работа программы зависит от порядка выполнения различных фрагментов кода, называются race condition или data race (состояние "гонки" или "гонки" вычислительных потоков; подразумевается, что имеют место несинхронизированные обращения к памяти). Для поиска таких ошибок необходимы специальные программные средства. Одно из них - Intel Thread Checker. Адрес продукта: http://www.viva64.com/go.php?url=526. Данная программа поставляется как модуль к профилировщику Intel VTune Performance Analyzer, дополняя имеющиеся средства для работы с многопоточным кодом. Intel Thread Checker позволяет обнаружить как описанные выше ошибки, так и многие другие, например deadlocks ("тупики", места взаимной блокировки вычислительных нитей) и утечки памяти. После установки Intel Thread Checker в диалоге New Project приложения Intel VTune Performance Analyzer появится новая категория проектов — Threading Wizards (мастера для работы с потоками), среди которых будет Intel Thread Checker Wizard. Для запуска примера необходимо выбрать его, а в следующем окне мастера указать путь к запускаемой программе. После запуска программа начнет выполняться, а профилировщик соберет все сведения о работе приложения. Пример такой информации, выдаваемой Intel Thread Checker, приведен на рисунке 1.
  • 4.
  • 5. Рисунок 1 - В результате работы Thread Checker обнаружено множество критических ошибок Как видно, даже для такой небольшой программы количество ошибок достаточно велико. Thread Checker группирует обнаруженные ошибки, одновременно оценивая их критичность для работы программы, и приводит их описание, что существенно повышает эффективность работы программиста. Кроме того, на вкладке Source View представлен программный код приложения с указанием тех мест в коде, где имеются ошибки (рисунок 2).
  • 6.
  • 7. Рисунок 2 - Анализ многопоточного кода Intel Thread Checker Следует учитывать, что Intel Thread Checker в ряде случаях не может выявить ошибку. Это относится к коду, который редко получает управление или выполняется на системе с другой архитектурой. Ошибка также может быть пропущена, когда набор входных тестовых данных сильно отличается от данных обрабатываемых программой при ее эксплуатации конечными пользователями. Все это не позволяет быть уверенным в отсутствии ошибок в многопоточной программе, после проверки ее с использованием динамических средств анализа, результат которых зависит от среды и времени исполнения.
  • 8. Но хорошей новостью для разработчиков OpenMP является существование и другого инструмента - VivaMP, предлагающего альтернативный подход к верификации параллельных программ. VivaMP построен по принципу статического анализатора кода и позволяет проверять код приложения без его запуска. Более подробно с инструментом VivaMP можно познакомиться на сайте разработчиков http://www.viva64.com/ru/vivamp-tool/. Области применения VivaMP: • Контроль корректности кода разрабатываемых приложений на базе технологии OpenMP. • Помощь в освоении технологии OpenMP и интеграция ее в уже существующие проекты. • Создание более эффективных в использовании ресурсов параллельных приложений. • Поиск ошибок в существующих OpenMP приложениях. Анализатор VivaMP интегрируется в среду Visual Studio 2005/2008 и предоставляет простой интерфейс для проверки приложений (рисунок 3).
  • 9. Рисунок 3 - Запуск инструмента VivaMP, интегрированного в Visual Studio 2005 VivaMP, Если мы запустим VivaMP для нашего примера, то получим сообщение об ошибках в 4 различных строках, где происходит некорректная модификация переменных (рисунок 4). Рисунок 4 - Результат работы статического анализатора VivaMP анализатора Конечно, статический анализ также имеет ряд недостатков, как и динамический анализ. Но вместе эти две методологии (два инструмента Intel Thread Checker и VivaMP) отлично дополнят друг друга. И их совместное использование является достаточно надежным методом выявления надежным ошибок в многопоточных приложениях. Описанную выше и обнаруженную средствами Intel Thread Checker и VivaMP ошибку записи в переменные x и y исправить довольно просто: нужно лишь добавить в конструкцию #pragma omp parallel for еще одну директиву private (x, y). Таким образом, эти две переменные будут директиву: объявлены как частные, и в каждом вычислительном потоке будут свои копии x и y. Следует также обратить внимание, что все потоки сохраняют вычисленный результат добавлением его к переменной s. Подобные ошибки происходят тогда, когда один вычислительный поток пытается нной записать некоторое значение в общую память, а другой в то же время выполняет операцию чтения. В рассматриваемом примере это может привести к некорректному результату. Рассмотрим инструкцию s += j*y. Изначально предполагается, что каждый поток суммирует трим вычисленный результат с текущим значением переменной s, а потом такие же действия выполняют остальные потоки. Однако возможна ситуация, когда, например, два потока одновременно начали выполнять инструкцию s += j*y, т. е. каждый из них сначала прочитает текущее значение переменной s, затем прибавит к этому значению результат умножения j*y и полученное запишет в общую переменную s. В отличие от операции чтения, которая может быть реализована параллельно и является достаточно быстрой, операция записи всегда последовательна. Следовательно, если сначала
  • 10. первый поток записал новое значение, то второй поток, выполнив после этого запись, затрет результат вычислений первого, потому что оба вычислительных потока сначала прочитали одно и то же значение s, а потом стали записывать свои данные в эту переменную. Иными словами, то значение s, которое второй поток в итоге запишет в общую память, никак не учитывает результат вычислений, полученный в первом потоке. Можно избежать подобной ситуации, если гарантировать, что в любой момент времени операцию s += j*y разрешается выполнять только одному из потоков. Такие операции называются неделимыми или атомарными. Когда нужно указать компилятору, что какая-либо инструкция является атомарной, используется конструкция #pragma omp atomic. Программный код, в котором исправлены указанные ошибки, приведен в листинге 3. double FixedFunctionOpenMP(int N) { double x, y, s=0; #pragma omp parallel for private(x,y) num_threads(2) for (int i=1; i<=N; i++) { x = (double)i/N; y = x; for (int j=1; j<=N; j++) { #pragma omp atomic s += j * y; y = y * x; }; }; return s; } После перекомпиляции программы и ее повторного анализа в Thread Checker мы увидим, что программа не содержит критических ошибок. Выводятся только два информационных сообщения о том, что параллельные потоки завершаются при достижении оператора return в функции MathFunction. В рассматриваемом примере так и должно быть, потому что распараллеливается только код внутри данной функции. Статический анализатор VivaMP не выдаст на этот код вообще никаких диагностических сообщений, так как он полностью корректен с его точки зрения. Но отдыхать еще рано. Давайте уточним, действительно ли наш код стал более эффективным после распараллеливания. Замерим время выполнения трех функций: 1 - последовательной, 2 - параллельной некорректной, 3 - параллельной корректной. Результаты такого измерения для N=15000 приведены в таблице 1.
  • 11. Функция Результат Время выполнения Последовательный вариант функции 287305025.528 0.5781 секунд Некорректный вариант параллельной функции 298441282.231 2.9531 секунд Корректный вариант параллельной функции, использующий 287305025.528 36.8281 секунд директиву atomic Таблица 1 - Результат работы функций И что мы видим в таблице? А то, что параллельный вариант некорректной функции работает в несколько раз медленнее. Но нас эта функция не интересует. Беда в том, что правильный вариант работает вообще более чем в 60 раз медленнее. Нам нужна такая параллельность? Конечно, нет. Все дело в том, что мы выбрали крайне неэффективный метод решения проблемы с суммированием результата в переменной s, использованием директивы atomic. Такой подход приводит к частому ожиданию потоками друг друга. Чтобы избежать постоянных взаимных блокировок при выполнении атомарной операции суммирования мы можем использовать специальную директиву reduction. Опция reduction определяет, что на выходе из параллельного блока переменная получит комбинированное значение. Допустимы следующие операции: +, *, -, &, |, ^, &&, ||. Модифицированный вариант функции показан в листинге 4. double OptimizedFunction(int N) { double x, y, s=0; #pragma omp parallel for private(x,y) num_threads(2) reduction(+: s) for (int i=1; i<=N; i++) { x = (double)i/N; y = x; for (int j=1; j<=N; j++) { s += j * y; y = y * x; }; }; return s; } На этот мы получим не только корректный, но и более производительный вариант функции (таблица 2). Скорость вычисления возросла почти в 2 раза (в 1.85 раз), что является очень хорошим показателем для подобных функций. Функция Результат Время
  • 12. выполнения Последовательный вариант функции 287305025.528 0.5781 секунд Некорректный вариант параллельной функции 298441282.231 2.9531 секунд Корректный вариант параллельной функции, использующий 287305025.528 36.8281 секунд директиву atomic Корректный вариант параллельной функции, использующий 287305025.528 0.3125 секунд директиву reduction Таблица 2 - Результат работы функций В заключение еще раз хочется подчеркнуть, что работоспособная параллельная программа может далеко не всегда являться эффективной. И хотя параллельное программирование предоставляет множество способов повышения эффективности кода, оно требует от программиста внимательности и хороших знаний используемых им технологий. К счастью существуют такие инструменты, как Intel Thread Checker и VivaMP, существенно облегчающих создание и проверки многопоточных приложений. Удачи вам уважаемые читатели в освоении новой области знаний. Приложение N1. Текст демонстрационной программы #include "stdafx.h" #include <omp.h> #include <stdlib.h> #include <windows.h> class VivaMeteringTimeStruct { public: VivaMeteringTimeStruct() { m_userTime = GetCurrentUserTime(); } ~VivaMeteringTimeStruct() { printf("Time = %.4f secondsn", GetUserSeconds()); } double GetUserSeconds(); private: __int64 GetCurrentUserTime() const; __int64 m_userTime; }; __int64 VivaMeteringTimeStruct::GetCurrentUserTime() const { FILETIME creationTime, exitTime, kernelTime, userTime; GetThreadTimes(GetCurrentThread(), &creationTime,
  • 13. &exitTime, &kernelTime, &userTime); __int64 curTime; curTime = userTime.dwHighDateTime; curTime <<= 32; curTime += userTime.dwLowDateTime; return curTime; } double VivaMeteringTimeStruct::GetUserSeconds() { __int64 delta = GetCurrentUserTime() - m_userTime; return double(delta) / 10000000.0; } double Function(int N) { double x, y, s=0; for (int i=1; i<=N; i++) { x = (double)i/N; y = x; for (int j=1; j<=N; j++) { s += j * y; y = y * x; }; }; return s; } double FunctionOpenMP(int N) { double x, y, s=0; #pragma omp parallel for num_threads(2) for (int i=1; i<=N; i++) {
  • 14. x = (double)i/N; y = x; for (int j=1; j<=N; j++) { s += j * y; y = y * x; }; }; return s; } double FixedFunctionOpenMP(int N) { double x, y, s=0; #pragma omp parallel for private(x,y) num_threads(2) for (int i=1; i<=N; i++) { x = (double)i/N; y = x; for (int j=1; j<=N; j++) { #pragma omp atomic s += j * y; y = y * x; }; }; return s; } double OptimizedFunction(int N) { double x, y, s=0; #pragma omp parallel for private(x,y) num_threads(2) reduction(+: s) for (int i=1; i<=N; i++) {
  • 15. x = (double)i/N; y = x; for (int j=1; j<=N; j++) { s += j * y; y = y * x; }; }; return s; } int _tmain(int , _TCHAR* []) { int N = 15000; { VivaMeteringTimeStruct Timer; printf("Result = %.3f ", Function(N)); } { VivaMeteringTimeStruct Timer; printf("Result = %.3f ", FunctionOpenMP(N)); } { VivaMeteringTimeStruct Timer; printf("Result = %.3f ", FixedFunctionOpenMP(N)); } { VivaMeteringTimeStruct Timer; printf("Result = %.3f ", OptimizedFunction(N)); } return 0; }