Другим важным этапом в технологическом цикле разработки параллельных программ является трассировка и профилирование. В процессе компиляции трассировку можно задать посредством опций: -mpitrace. При запуске параллельного приложения на стандартное устройство вывода поступает текстовая информация о последовательности вызовов MPI-функции в формате:
[номер процесса] старт функции…
[номер процесса] завершение функции…
Для более детального разбора полученной информации удобнее перенаправить вывод в файл, например trace.log(пример файла приведен на рис. 8).
Зачастую простой разбор этого файла помогает обнаружить ошибки в параллельных программах. Во-первых, каждая функция в случае успешного выполнения порождает пару записей в файле: “старт” и ”завершение” функции, в случае неуспешного выполнения выведется информация лишь о старте этой функции. Во-вторых, некоторые функции (например,MPI_Send,MPI_Recv) выводят параметры их запуска (например, count – количество отправляемых или получаемых элементов, dest – адрес получателя, source – адрес отправителя, tag– идентификатор сообщения и т.д.), которые могут помочь разобраться в правильности использования функций для посылки и приема сообщений.
Опция –mpitraceпомогает отследить ошибки, появляющиеся при неправильном использовании функций.
Производительность MPI-приложений можно оценить путем анализа файла *.clog, порождаемого, если на этапе компиляции будет задана опция –mpilog.
В результате выполнения параллельного приложения создается файл (например,example.clog), который необходимо сконвертировать в файл example.slog утилитой MPI clog2slog. Получившийся в результате файл пригоден для визуального просмотра с помощью утилиты logviewer.
На рисунке 9 представлено окно программы, содержащее в визуальном виде трассу исполнения параллельной программы, с графическим отображением различных функций MPI и их длительностей. На рисунке представлена визуализация пересылок и их длительности при запуске параллельного приложения на 4 процессорах. Номер процессора обозначен на левой и правой сторонах картинки, снизу – временная шкала в секундах, стрелками помечаются направления пересылок, цветом различаются используемые коммуникационные функции.
Starting MPI_Init...
Starting MPI_Init...
Starting MPI_Init...
Starting MPI_Init...
[0] Ending MPI_Init
[0] Starting MPI_Comm_size...
[0] Ending MPI_Comm_size
[0] Starting MPI_Comm_rank...
[0] Ending MPI_Comm_rank
Hello, 0 processor of 4...work
[0] Starting MPI_Send with count=4, dest=1, tag=1...
[1] Ending MPI_Init
[2] Ending MPI_Init
[3] Ending MPI_Init
[0] Ending MPI_Send
[0] Starting MPI_Recv with count=4, source=3, tag=1...
[1] Starting MPI_Comm_size...
[1] Ending MPI_Comm_size
[1] Starting MPI_Comm_rank...
[1] Ending MPI_Comm_rank
Hello, 1 processor of 4...work
[1] Starting MPI_Recv with count=4, source=0, tag=1...
[1] Ending MPI_Recv from 0 with tag 1
[1] Starting MPI_Send with count=0, dest=2, tag=1...
[1] Ending MPI_Send
[1] Starting MPI_Finalize...
[2] Starting MPI_Comm_size...
[2] Ending MPI_Comm_size
[2] Starting MPI_Comm_rank...
[2] Ending MPI_Comm_rank
Hello, 2 processor of 4...work
[2] Starting MPI_Recv with count=4, source=1, tag=1...
[3] Starting MPI_Comm_size...
[3] Ending MPI_Comm_size
[3] Starting MPI_Comm_rank...
[3] Ending MPI_Comm_rank
Hello, 3 processor of 4...work
[3] Starting MPI_Recv with count=4, source=2, tag=1...
Рис. 8. Пример трассировки параллельной программы