Отладка параллельной программы с использованием Intel Thread Checker
11.2.1.6. Анализ параллельной реализации 2
Откройте проект Scalar2. Соберите программу в конфигурации debug. Прежде всего, посмотрим на результаты работы последовательной и параллельной версии. Зададим размер векторов равный 100 и запустим программу.
Итак, результаты запуска однозначно указывают на некорректность параллельной версии. Приступаем к поиску ошибок.
Снова выполните действия, описанные в пункте 7.3 документа "ITC - Краткое описание.doc", чтобы подготовить программу для анализа в ITC. Запустите ITC, создайте в нем новый проект и укажите путь к исполняемому файлу C:\ITCLabs\Scalar2\Debug\Scalar2.exe.
Нажмите кнопку Finish. После этого запустится ITC, произведет инструментацию программы и начнет анализ. Результатом станет появление следующей диагностики:
Первое, что можно отметить - появились новые типы сообщений. Вернемся к этому позже. Второй интересный момент - ошибка за номером 1.
"Открываем" ее двойным щелчком:
Представленная диагностика вызвана тем, что ITC "не понимает" run-time функции OpenMP и в частности omp_get_num_threads(). Отсюда, кстати, и большая часть остальных ошибок, которые вызваны тем, что значение переменной rank не известно ITC, а соответственно, он находит конфликты там, где их на самом деле нет.
Выходов из подобных ситуаций два: отказаться от использования run-time функций OpenMP (тем более, что их использование при программировании на OpenMP считается чем-то вроде плохого тона) или отказаться от компиляторного инструментирования, то есть удалить из опций проекта ключ /Qtcheck.
Но прежде вернемся к ошибке под номером 9.
Несмотря на не слишком вразумительное сообщение, тем не менее, можно понять, что выше по коду нами допущена ошибка - переменная 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.
Теперь мы, к сожалению, потеряли информацию об именах переменных. Число сообщений также сократилось. Однако ITC все еще видит в коде ошибки.
Анализируя первое же сообщение, мы находим одну из типичных ошибок в многопоточных программах - использование потоками неинициализированных данных.
Глядя на выделенные строки, нетрудно понять, что поток с номером, отличным от 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 подтвердит отсутствие проблем в коде.