Опубликован: 28.10.2009 | Доступ: свободный | Студентов: 515 / 40 | Оценка: 4.67 / 4.39 | Длительность: 20:33:00
Самостоятельная работа 4:

Отладка параллельной программы с использованием Intel Thread Checker

< Лекция 7 || Самостоятельная работа 4: 123456 || Лекция 8 >
11.2.1.6. Анализ параллельной реализации 2

Откройте проект Scalar2. Соберите программу в конфигурации debug. Прежде всего, посмотрим на результаты работы последовательной и параллельной версии. Зададим размер векторов равный 100 и запустим программу.

Результат запуска параллельной реализации 2

увеличить изображение
Рис. 11.5. Результат запуска параллельной реализации 2

Итак, результаты запуска однозначно указывают на некорректность параллельной версии. Приступаем к поиску ошибок.

Снова выполните действия, описанные в пункте 7.3 документа "ITC - Краткое описание.doc", чтобы подготовить программу для анализа в ITC. Запустите ITC, создайте в нем новый проект и укажите путь к исполняемому файлу C:\ITCLabs\Scalar2\Debug\Scalar2.exe.

Нажмите кнопку Finish. После этого запустится ITC, произведет инструментацию программы и начнет анализ. Результатом станет появление следующей диагностики:

Результат анализа параллельной реализации 2 скалярного умножения

Рис. 11.6. Результат анализа параллельной реализации 2 скалярного умножения

Первое, что можно отметить - появились новые типы сообщений. Вернемся к этому позже. Второй интересный момент - ошибка за номером 1.

"Открываем" ее двойным щелчком:

Просмотр диагностики "Use of unsupported API"

увеличить изображение
Рис. 11.7. Просмотр диагностики "Use of unsupported API"

Представленная диагностика вызвана тем, что ITC "не понимает" run-time функции OpenMP и в частности omp_get_num_threads(). Отсюда, кстати, и большая часть остальных ошибок, которые вызваны тем, что значение переменной rank не известно ITC, а соответственно, он находит конфликты там, где их на самом деле нет.

Выходов из подобных ситуаций два: отказаться от использования run-time функций OpenMP (тем более, что их использование при программировании на OpenMP считается чем-то вроде плохого тона) или отказаться от компиляторного инструментирования, то есть удалить из опций проекта ключ /Qtcheck.

Но прежде вернемся к ошибке под номером 9.

Неверная локализация переменной sum

увеличить изображение
Рис. 11.8. Неверная локализация переменной sum

Несмотря на не слишком вразумительное сообщение, тем не менее, можно понять, что выше по коду нами допущена ошибка - переменная sum сделана private, что, конечно же, неверно. Этот массив специально был создан для подсчета потоками локальных сумм и естественно должен быть общим, чтобы по окончании параллельной секции из него в переменную sum_all можно было собрать итоговую сумму.

Исправляем ситуацию:

#pragma omp parallel private(rank/*, sum*/)
{
  ...
}
sum_all = 0;
for (i = 0; i < NumThreads; i++)
{
  sum_all += sum[i];
}

DeleteVector(x);
DeleteVector(y);

Убираем ключ /Qtcheck из проекта и снова запускаем анализ в ITC.

Результат анализа параллельной реализации 2 после первого исправления

Рис. 11.9. Результат анализа параллельной реализации 2 после первого исправления

Теперь мы, к сожалению, потеряли информацию об именах переменных. Число сообщений также сократилось. Однако ITC все еще видит в коде ошибки.

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

Ошибка - использование потоками неинициализированных данных

увеличить изображение
Рис. 11.10. Ошибка - использование потоками неинициализированных данных

Глядя на выделенные строки, нетрудно понять, что поток с номером, отличным от 0, будет пытаться использовать переменные x и y до того, как нулевой поток выделит под них память, что в лучшем случае приведет к падению, а в худшем к неверному результату расчетов. Решение в данной ситуации также является типичным и состоит в установке барьера после участка инициализации.

#pragma omp parallel private(rank)
{
  rank = omp_get_thread_num();
  if (rank == 0)
  {
    NumThreads = omp_get_num_threads();
    x = CreateVector(N);
    y = CreateVector(N);
  }
  sum[rank] = 0;
  #pragma omp barrier
  #pragma omp for
  for (i = 0; i < N; i++)
  {
    sum[rank] += x[i] * y[i];
  }
}
...

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

< Лекция 7 || Самостоятельная работа 4: 123456 || Лекция 8 >