Ошибка на уровне mpi undefined error

Ошибка на уровне mpi undefined error Реализация MPI может обрабатывать или не обрабатывать некоторые ошибки, которые возникают при выполнении вызовов MPI . Это могут быть ошибки, которые генерируют исключения или прерывания, например, ошибки для операций с плавающей точкой или при нарушении доступа. Набор ошибок, которые корректно обрабатываются MPI , зависит от реализации. Каждая такая […]

Содержание

  1. Ошибка на уровне mpi undefined error
  2. Ошибка на уровне mpi undefined error
  3. Ошибка на уровне mpi undefined error

Ошибка на уровне mpi undefined error

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

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

Пользователь может связывать обработчик ошибок с коммуникатором. Эта процедура будет использоваться для любого исключения MPI , которое имеет место в течение вызова MPI для обмена с использованием этого коммуникатора. Вызовы MPI , которые не связаны ни с одним коммуникатором, рассматриваются, как относящиеся к коммуникатору MPI_COMM_WORLD .

Привязка обработчиков ошибок к коммуникатору исключительно локальная: различные процессы могут присоединить различные обработчики ошибок к тому же самому коммуникатору.

Вновь созданный коммуникатор наследует обработчик ошибок, который связан с «родительским» коммуникатором. В частности, пользователь может определить «глобальный» обработчик ошибок для всех коммуникаторов, связывая этот обработчик с коммуникатором MPI_COMM_WORLD сразу после инициализации.

В MPI доступны несколько предопределенных обработчиков ошибок :

    MPI_ERRORS_ARE_FATAL — обработчик, который после вызова прерывает работу программы на всех процессах. Это имеет тот же эффект, как если бы процессом, который запустил обработчик, был вызван MPI_ABORT .
  • MPI_ERRORS_RETURN — обработчик не делает ничего, кроме представления кода ошибки пользователю.
  • Реализации могут обеспечивать дополнительные обработчики ошибок, программисты также могут написать свои собственные обработчики ошибок.

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

    После того, как ошибка обнаружена, состояние MPI является неопределенным. Это означает, что даже если используется определенный пользователем обработчик ошибок или
    MPI_ERRORS_RETURN , не обязательно , что пользователю будет разрешено продолжить использовать MPI после того, как ошибка определена. Цель таких обработчиков состоит в том, чтобы пользователь получил определенное им сообщение об ошибке и предпринял действия, не относящиеся к MPI (такие, как очистки буферов ввода/вывода) перед выходом из программы. Реализация MPI допускает продолжение работы приложения после возникновения ошибки, но не требует, чтобы так было всегда.

    Совет разработчикам: Хорошие реализации MPI должны максимально ограничивать воздействие ошибки, чтобы нормальное функционирование могло продолжаться после того, как обработчик ошибок был запущен. В документации должна содержаться информация относительно возможного эффекта по каждому классу ошибок.[]

    Обработчик ошибок MPI является скрытым объектом, связанным с дескриптором. Процедуры MPI обеспечивают создание новых обработчиков ошибок, связывают обработчики ошибок с коммуникаторами и проверяют, какой обработчик ошибок связан с коммуникатором.

    Синтаксис функции MPI_ERRHANDLER_CREATE представлен ниже.

    IN function установленная пользователем процедура обработки ошибок
    OUT errhandler MPI обработчик ошибок (дескриптор)

    int MPI_Errhandler_create(MPI_Handler_function *function,
    MPI_Errhandler *errhandler)

    MPI_ERRHANDLER_CREATE(FUNCTION, ERRHANDLER, IERROR)
    EXTERNAL FUNCTION
    INTEGER ERRHANDLER, IERROR

    Функция MPI_ERRHANDLER_CREATE регистрирует процедуру пользователя в качестве обработчика MPI исключений. Возвращает в errhandler дескриптор зарегистрированного обработчика исключений.

    В языке Си процедура пользователя должна быть функцией типа MPI_Handler_function , которая определяется как:

    Первый аргумент является идентификатором используемого коммуникатора, второй является кодом ошибки, который будет возвращен процедурой MPI , которая выявила ошибку. Если процедура возвратила MPI_ERR_IN_STATUS , то это значит, что код ошибки возвращен в статусный объект обращения, которое запустило обработчик ошибок. Остающиеся аргументы есть аргументы « stdargs », номер и значение которых являются зависимыми от реализации. В реализации должны быть ясно документированы эти аргументы. Адреса используются так, чтобы обработчик мог быть написан на языке ФОРТРАН.

    Синтаксис функции MPI_ERRHANDLER_SET представлен ниже.

    IN comm коммуникатор , на котором устанавливается обработчик ошибок (дескриптор)
    IN errhandler новый обработчик ошибок для коммуникатора (дескриптор)

    int MPI_Errhandler_set(MPI_Comm comm, MPI_Errhandler errhandler)

    MPI_ERRHANDLER_SET(COMM, ERRHANDLER, IERROR)
    INTEGER COMM, ERRHANDLER, IERROR

    Функция MPI_ERRHANDLER_SET связывает новый обработчик ошибок errorhandler с коммуникатором comm на вызывающем процессе. Заметим, что обработчик ошибок всегда связан с коммуникатором.

    Синтаксис функции MPI_ERRHANDLER_GET представлен ниже.

    IN comm коммуникатор, из которого получен обработчик ошибок (дескриптор)
    OUT errhandler MPI обработчик ошибок, связанный с коммуникатором (дескриптор)

    int MPI_Errhandler_get(MPI_Comm comm, MPI_Errhandler *errhandler)

    MPI_ERRHANDLER_GET(COMM, ERRHANDLER, IERROR)
    INTEGER COMM, ERRHANDLER, IERROR

    Функция MPI_ERRHANDLER_GET возвращает в errhandler дескриптор обработчика ошибок, связанного с коммуникатором comm. Пример: библиотечная функция может записать на входной точке текущий обработчик ошибок для коммуникатора, затем установить собственный частный обработчик ошибок для этого коммуникатора и восстановить перед выходом предыдущий обработчик ошибок.

    Синтаксис функции MPI_ERRHANDLER_FREE представлен ниже.

    INOUT errhandler MPI обработчик ошибок (дескриптор)

    int MPI_Errhandler_free(MPI_Errhandler *errhandler)

    MPI_ERRHANDLER_FREE(ERRHANDLER, IERROR)
    INTEGER ERRHANDLER, IERROR

    Эта функция маркирует обработчик ошибок, связанный с errhandler для удаления и устанавливает для errhandler значение MPI_ERRHANDLER_NULL . Обработчик ошибок будет удален после того, как все коммуникаторы, связанные с ним, будут удалены.

    Синтаксис функции MPI_ERROR_STRING представлен ниже.

    MPI_ERROR_STRING(errorcode, string, resultlen)

    IN errorcodeк код ошибки, возвращаемый процедурой MPI
    OUT string текст, соответствующий errorcode
    OUT resultlen длина (в печатных знаках) результата, возвращаемого в string

    int MPI_Error_string(int errorcode, char *string, int *resultlen)

    MPI_ERROR_STRING(ERRORCODE, STRING, RESULTLEN, IERROR)
    INTEGER ERRORCODE, RESULTLEN, IERROR
    CHARACTER*(*) STRING

    void MPI::Get_error_string(int errorcode, char* name, int& resulten)

    Функция MPI_ERROR_STRING возвращает текст ошибки, связанный с кодом или классом ошибки. Аргумент string обязан иметь длину не менее _MAX_ERROR_STRING знаков. Число фактически записанных символов возвращается в выходном аргументе resultlen .

    Объяснение: Форма этой функции была выбрана такой для того, чтобы сделать привязки в языке ФОРТРАН и Си похожими. Версия, которая возвращает указатель на строку, создает две проблемы. Во первых, возвращенная строка должна быть статически распределена и различаться для каждого сообщения об ошибке (позволяя указателям, возвращенным успешными обращениями к MPI_ERROR_STRING , указать правильное сообщение). Во вторых, в языке ФОРТРАН функция, объявленная, как возвращающая CHARACTER*(*), может не ссылаться, например, на оператор PRINT .[]

    Next: Коды и классы ошибок. Up: Управление исполнительной средой MPI Previous: Получение сведений об исполнительной &nbsp Contents Alex Otwagin 2002-12-10

    Источник

    Ошибка на уровне mpi undefined error

    Коды ошибок, возвращаемых MPI , полностью приведены в реализации MPI (за исключением MPI_SUCCESS ). Это сделано для того, чтобы позволить реализации представить как можно больше информации об ошибках (для использования с MPI_ERROR_STRING ).

    Чтобы приложения могли интерпретировать код ошибки, процедура MPI_ERROR_CLASS преобразует код любой ошибки в один из кодов небольшого набора кодов стандартных ошибок, названный классом ошибок . Правильные классы ошибок удовлетворяют условию:

    Классы ошибок являются подмножеством кодов ошибок: функция MPI может возвращать номер класса ошибки, а функция MPI_ERROR_STRING может использоваться, чтобы вычислить строку ошибки, связанную с классом ошибки.

    Коды ошибок удовлетворяют выражению:

    0 = const MPI_SUCCESS

    Объяснение: Разница между MPI_ERR_UNKNOWN и MPI_ERR_OTHER состоит в том, что
    MPI_ERROR_STRING может возвращать полезную информацию о MPI_ERR_OTHER .

    Заметим, что MPI_SUCCESS = 0 необходимо для соответствия языку Си; разделение классов ошибки и кодов ошибки допускает определение класса ошибки этим способом. Наличие известного LASTCODE — часто хороший контроль готовности к работе.[]

    MPI_SUCCESS Ошибки нет
    MPI_ERR_BUFFER Неправильный указатель буфера
    MPI_ERR_COUNT Неверное количество аргумента
    MPI_ERR_TYPE Неправильный тип аргумента
    MPI_ERR_TAG Неправильный тэг аргумента
    MPI_ERR_COMM Неправильный коммуникатор
    MPI_ERR_RANK Неправильный номер
    MPI_ERR_REQUEST Неверный запрос (дескриптор)
    MPI_ERR_ROOT Неверный корневой идентификатор
    MPI_ERR_GROUP Неправильная группа
    MPI_ERR_OP Неправильная операция
    MPI_ERR_TOPOLOGY Неверная топология
    MPI_ERR_DIMS Неправильная размерность аргумента
    MPI_ERR_ARG Ошибка аргумента некоторого другого типа
    MPI_ERR_UNKNOWN Неизвестная ошибка
    MPI_ERR_TRUNCATE Неправильное округление
    MPI_ERR_OTHER Известная ошибка не из этого списка
    MPI_ERR_INTERN Внутренняя ошибка реализации MPI
    MPI_ERR_IN_STATUS Неправильный код статуса
    MPI_ERR_PENDING Зависший запрос
    MPI_ERR_LASTCODE Последний код в списке

    Синтаксис функции MPI_ERROR_CLASS представлен ниже.

    IN errorcode код ошибки, возвращаемый процедурой MPI
    OUT errorclass класс ошибки, связанный с errorcode

    int MPI_Error_class(int errorcode, int *errorclass)

    MPI_ERROR_CLASS(ERRORCODE, ERRORCLASS, IERROR)
    INTEGER ERRORCODE, ERRORCLASS, IERROR

    int MPI::Get_error_class(int errorcode)

    Функция MPI_ERROR_CLASS отображает код каждой стандартной ошибки (класс ошибки) на себя.

    Subsections

    • Устаревшие функции

    Next: Устаревшие функции Up: Управление исполнительной средой MPI Previous: Обработка ошибок &nbsp ContentsAlex Otwagin 2002-12-10

    Источник

    Ошибка на уровне mpi undefined error

    The error codes returned by MPI are left entirely to the implementation (with the exception of MPI_SUCCESS). This is done to allow an implementation to provide as much information as possible in the error code (for use with MPI_ERROR_STRING).

    To make it possible for an application to interpret an error code, the routine MPI_ERROR_CLASS converts any error code into one of a small set of standard error codes, called error classes. Valid error classes are shown in Table 6 and Table 7 .

    MPI_ERR_KEYVAL

    MPI_ERR_NO_MEM

    MPI_ERR_INFO_KEY

    MPI_ERR_SPAWN

    MPI_ERR_WIN

    MPI_SUCCESS No error
    MPI_ERR_BUFFER Invalid buffer pointer
    MPI_ERR_COUNT Invalid count argument
    MPI_ERR_TYPE Invalid datatype argument
    MPI_ERR_TAG Invalid tag argument
    MPI_ERR_COMM Invalid communicator
    MPI_ERR_RANK Invalid rank
    MPI_ERR_REQUEST Invalid request (handle)
    MPI_ERR_ROOT Invalid root
    MPI_ERR_GROUP Invalid group
    MPI_ERR_OP Invalid operation
    MPI_ERR_TOPOLOGY Invalid topology
    MPI_ERR_DIMS Invalid dimension argument
    MPI_ERR_ARG Invalid argument of some other kind
    MPI_ERR_UNKNOWN Unknown error
    MPI_ERR_TRUNCATE Message truncated on receive
    MPI_ERR_OTHER Known error not in this list
    MPI_ERR_INTERN Internal MPI (implementation) error
    MPI_ERR_IN_STATUS Error code is in status
    MPI_ERR_PENDING Pending request
    Invalid keyval has been passed
    MPI_ALLOC_MEM failed because memory is exhausted
    MPI_ERR_BASE Invalid base passed to MPI_FREE_MEM
    Key longer than MPI_MAX_INFO_KEY
    MPI_ERR_INFO_VALUE Value longer than MPI_MAX_INFO_VAL
    MPI_ERR_INFO_NOKEY Invalid key passed to MPI_INFO_DELETE
    Error in spawning processes
    MPI_ERR_PORT Invalid port name passed to MPI_COMM_CONNECT
    MPI_ERR_SERVICE Invalid service name passed to MPI_UNPUBLISH_NAME
    MPI_ERR_NAME Invalid service name passed to MPI_LOOKUP_NAME
    Invalid win argument
    MPI_ERR_SIZE Invalid size argument
    MPI_ERR_DISP Invalid disp argument
    MPI_ERR_INFO Invalid info argument
    MPI_ERR_LOCKTYPE Invalid locktype argument
    MPI_ERR_ASSERT Invalid assert argument
    MPI_ERR_RMA_CONFLICT Conflicting accesses to window
    MPI_ERR_RMA_SYNC Wrong synchronization of RMA calls

    Table 6: Error classes (Part 1)

    MPI_ERR_FILE Invalid file handle
    MPI_ERR_NOT_SAME Collective argument not identical on all processes, or collective routines called in a different order by different processes
    MPI_ERR_AMODE Error related to the amode passed to MPI_FILE_OPEN
    MPI_ERR_UNSUPPORTED_DATAREP Unsupported datarep passed to MPI_FILE_SET_VIEW
    MPI_ERR_UNSUPPORTED_OPERATION Unsupported operation, such as seeking on a file which supports sequential access only
    MPI_ERR_NO_SUCH_FILE File does not exist
    MPI_ERR_FILE_EXISTS File exists
    MPI_ERR_BAD_FILE Invalid file name (e.g., path name too long)
    MPI_ERR_ACCESS Permission denied
    MPI_ERR_NO_SPACE Not enough space
    MPI_ERR_QUOTA Quota exceeded
    MPI_ERR_READ_ONLY Read-only file or file system
    MPI_ERR_FILE_IN_USE File operation could not be completed, as the file is currently open by some process
    MPI_ERR_DUP_DATAREP Conversion functions could not be registered because a data representation identifier that was already defined was passed to MPI_REGISTER_DATAREP
    MPI_ERR_CONVERSION An error occurred in a user supplied data conversion function.
    MPI_ERR_IO Other I/O error
    MPI_ERR_LASTCODE Last error code

    Table 7: Error classes (Part 2)

    The error classes are a subset of the error codes: an MPI function may return an error class number; and the function MPI_ERROR_STRING can be used to compute the error string associated with an error class. An MPI error class is a valid MPI error code. Specifically, the values defined for MPI error classes are valid MPI error codes.

    The error codes satisfy,

    The difference between MPI_ERR_UNKNOWN and MPI_ERR_OTHER is that MPI_ERROR_STRING can return useful information about MPI_ERR_OTHER.

    Note that MPI_SUCCESS = 0 is necessary to be consistent with C practice; the separation of error classes and error codes allows us to define the error classes this way. Having a known LASTCODE is often a nice sanity check as well. ( End of rationale.)

    MPI_ERROR_CLASS( errorcode, errorclass )
    IN errorcode Error code returned by an MPI routine
    OUT errorclass Error class associated with errorcode

    int MPI_Error_class(int errorcode, int *errorclass)

    MPI_ERROR_CLASS(ERRORCODE, ERRORCLASS, IERROR)
    INTEGER ERRORCODE, ERRORCLASS, IERROR
    int MPI::Get_error_class(int errorcode)
    The function MPI_ERROR_CLASS maps each standard error code (error class) onto itself.

    Return to MPI-2.1 Standard Index
    Return to MPI Forum Home Page
    MPI-2.0 of July 1, 2008
    HTML Generated on July 6, 2008

    Источник

    MPI_Status status;

    int count;

    MPI_Recv( … , MPI_INT, … , &status );

    MPI_Get_count( &status, MPI_INT, &count );

    /* … теперь count содержит количество принятых ячеек */

    Обратите внимание, что аргумент-описатель типа у MPI_Recv и MPI_Get_count должен быть одинаковым, иначе, в зависимости от реализации в count вернется неверное значение; или произойдет ошибка времени выполнения.

    Константы-пустышки включают:

    • MPI_COMM_NULL;

    • MPI_DATATYPE_NULL;

    • MPI_REQUEST_NULL.

    Константа неопределенного значения используется в процедуре MPI_Comm_Split и имеет имя MPI_UNDEFINED.

    Константы глобальных операций используются в процедурах коллективного взаимодействия процессов для указания типа выполняемой операции. Данные константы включают:

    • MPI_MAX;

    • MPI_MIN;

    • MPI_SUM;

    • MPI_PROD.

    Константы, определяющие любой процесс/идентификатор, используются для обозначения

    • MPI_ANY_SOURCE;

    • MPI_ANY_TAG.

    В стандарте MPI существует несколько предопределенных типов, среди них:

    • MPI_Status — структура; атрибуты сообщений; содержит три обязательных поля:

    • MPI_Source (номер процесса отправителя);

    • MPI_Tag (идентификатор сообщения);

    • MPI_Error (код ошибки);

    • MPI_Request — системный тип; идентификатор операции посылки-приема сообщения;

    • MPI_Comm — системный тип; идентификатор группы (коммуникатора);

    • MPI_COMM_WORLD — зарезервированный идентификатор группы, состоящей их всех процессов приложения.

    3.8 Латентность и пропускная способность

    3.8.1 Понятия латентности и пропускной способности

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

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

    Для улучшения характеристик латентности и пропускной способности необходимо:

    • подсчитать кол-во каналов передачи данных между процессами при разработке крупномодульных приложений;

    • использовать архивацию данных для больших сообщений, а также использовать описываемые типы данных вместо MPI_PACK и MPI_UNPACK если возможно;

    • использовать при возможности коллективные операции; это устраняет вызов MPI_Send и MPI_RECV каждый раз при коммуникации процессов;

    • определять номер принимающего процесса при вызове подпрограммы MPI; использование MPI_ANY_SOURCE может увеличивать латентность;

    • использовать MPI_RECV_INIT и MPI_STARTALL вместо вызова MPI_Irecv в цикле в случаях, когда запросы/прием не могут быть выполнены сразу.

    Например, вы написали программу, содержащую фрагмент:

    j = 0

    for (i=0; i

    if (i==rank) continue;

    MPI_Irecv(buf[i], count, dtype, i, 0, comm, &requests[j++]);

    }

    MPI_Waitall(size-1, requests, statuses);

    Предположим, что одна из итераций с вызовом MPI_IRECV не завершилась перед следующей итерацией цикла. В этом случае, MPI пробует выполнить оба запроса. Это может продолжаться, приводя к большему времени ожидания. Чтобы избежать этого, можно переписать эту часть кода так:

    j = 0

    for (i=0; i

    if (i==rank) continue;

    MPI_Recv_init(buf[i], count, dtype, i, 0, comm,

    &requests[j++]);

    }

    MPI_Startall(size-1, requests);

    MPI_Waitall(size-1, requests, statuses);

    В этом случае все итерации с вызовом MPI_RECV_INIT выполняются только один раз при вызове MPI_STARTALL. При таком подходе вы не получите дополнительного времени ожидания при использовании MPI_Irecv и может улучшить латентность приложения.

    3.8.2 Выбор подпрограмм/функций MPI

    Для достижения наименьшей латентности и наибольшей пропускной способности сообщений для синхронной передачи «точка-точка», используйте блокирующие функции MPI MPI_Send и MPI_RECV. Для асинхронной передачи, используйте неблокирующие функции MPI MPI_Isend и MPI_IRECV.

    При использовании блокирующих функций, старайтесь избегать ожидающих запросов.

    Для задач требующих использования коллективных операций, используют соответствующую коллективную функцию MPI.

    4 Входные и выходные данные

    4.1 Этапы создания программ

    Создание параллельной программы состоит из следующих этапов:

    • последовательный алгоритм подвергается декомпозиции (распараллеливанию), т.е. разбивается на независимо работающие ветви; для взаимодействия в ветви вводятся две дополнительные нематематические операции: прием и передача данных;

    • распараллеленный алгоритм записывается в виде программы, в которой операции приема и передачи записываются в терминах конкретной системы связи между ветвями;

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

    4.2 Компиляция модулей

    Для компиляции и сборки программного модуля, написанного на С, используется команда mpicc. Аналогично для С++ используется команда mpiCC, для Fortran 77 используется mpif77, а для Fortran 90 – mpif90. Все команды пакета MPICH for GM настроены на использование компиляторов фирмы Compaq.

    Эти команды предусматривают некоторые опции и подключают специальные библиотеки, необходимые для компиляции и сборки программ MPI:

    • опция -с указывается для выполнения только компиляции файла, не создавая объектный файл;

    • при задании опции -о осуществляется сборка и компиляция, а также создается объектный и запускаемый файлы.

    Примеры

    1 Компиляция программного модуля myprog.c

    mpicc –c myprog.c

    2 Сборка и компиляция программного модуля myprog.c с созданием выходного файла myfile

    mpicc myprog.c –o myfile

    4.3 Запуск программ, использующих MPI

    Запуск на исполнение MPI-программы производится с помощью команды:

    mpirun –np [-h –maxtime –quantum -stdiodir] [параметры_программы…]

    Параметры команды mpirun слелующие:

    • параметр -h используется для выдачи интерактивной подсказки по параметрам команды mpirun;

    • параметр -np обозначает число процессоров, требуемое программе;

    • параметр -maxtime задает максимальное время счета. От этого времени зависит положение задачи в очереди. После истечения этого времени задача принудительно заканчивается;

    • параметр -quantum указывает, что задача является фоновой, и задает размер кванта для фоновой задачи;

    • параметр -stdiodir задает имя каталога стандартного вывода, в который будут записываться протокол запуска задачи, файл стандартного вывода и имена модулей, на которых запускалась задача.

    Более подробное описание параметров команды запуска задач и постановки задачи в очередь приведено в руководстве “Подсистема коллективного доступа к ресурсам СК” [7].

    5 Средства отладки и профилирования

    В общем случае отладка параллельных программ является достаточно сложной задачей и требует специальных инструментальных средств. В пакет mpich встроены некоторые дополнительные средства, которые могут использоваться при отладке и профилировании программ MPI.

    5.1 Обработчики ошибок

    Стандарт MPI определяет механизм для установки пользовательских обработчиков ошибок и определяет поведение двух встроенных обработчиков: MPI_ERRORS_RETURN и MPI_ERRORS_ARE_FATAL. В библиотеку mpe встроено еще два обработчика ошибок для облегчения использования отладчика dbx с программами, написанными с использованием стандарта MPI:

    MPE_Errors_call_dbx_in_xterm

    MPE_Signals_call_debugger

    Данные обработчики ошибок расположены в директории mpe, основного дерева каталогов MPICH. При конфигурировании MPICH с опцией -mpedbg, эти отладчики включаются в основные библиотеки MPICH, и появляется возможность (с помощью аргумента командной строки mpedbg) установить обработчик MPE_Errors_call_dbx_in_xterm вызываемым по умолчанию обработчиком ошибок (вместо MPI_ERRORS_ARE_FATAL). В приложении А приведен пример программы, обрабатывающей ошибки с помощью процедур MPI (tester.c).

    5.2 Профилировочные библиотеки

    Профилировочный интерфейс MPE (Message Passing Extensions) представляет собой инструмент для добавления процедур анализа производительности в любую MPI-программу. С пакетом MPICH поставляется три профилировочные библиотеки:

    • библиотека определения времени выполнения процедур MPI;

    • библиотека создания файла журнала и утилита UpShot;

    • библиотека анимации процесса работы программы в реальном времени.

    В Приложении А приведен пример программы с использованием профилировочных библиотек cpilog.c.

    5.2.1 Библиотека определения времени выполнения процедур MPI

    Данная библиотека достаточно проста. Профилировочная версия каждой процедуры MPI (MPI_Xxx) вызывает функцию PMPI_Wtime (возвращающую текущее время) перед и после каждого вызова соответствующей PMPI_Xxx процедуры. Времена накапливаются для каждого процесса и выводятся в файл (отдельный файл для каждого процесса) в профилировочной версии MPI_Finalize. В дальнейшем эти файлы можно использовать для создания отчета по всему приложению или по отдельным процессам. Текущая реализация библиотеки не обрабатывает вложенные циклы.

    5.2.2 Создание файла журнала и утилита UpShot

    Эта профилировочная библиотека предназначена для генерации файла журнала (log-файла), в котором фиксируются события, привязанные ко времени.

    Для сохранения определенных типов событий в памяти, в процессе выполнения вызывается процедура MPI_Log_event, а сборка и объединение этих частей памяти с информацией о событиях происходит в MPI_Finalize. Для остановки и перезапуска операций записи событий во время выполнения программы может использоваться процедура MPI_Pcontrol.

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

    Рисунок 1 – Вид файла журнала

    5.2.3 Анимация процесса работы программы в реальном времени

    Графическая библиотека MPE предоставляет возможности для простой анимации в реальном времени. Библиотека содержит процедуры, которые позволяют разделять X-дисплей нескольким процессам. На основе данной библиотеки существует возможность графически изображать процесс передачи сообщений и их интенсивность в процессе работы программы.

    Для сборки программы с использованием графических библиотек MPE при компиляции можно указать опцию –lmpe.

    В MPICH предусмотрены также опции для компиляции и сборки программ с различными профилировочными библиотеками MPE:

    -mpitrace

    Для компиляции и сборки с отладочными библиотеками.

    -mpianim

    Для компиляции и сборки с анимационными библиотеками.

    -mpilog

    Для компиляции и сборки с регистрирующими библиотеками .

    Пример

    mpif77 -mpilog -o fpilog fpilog.f

    Более подробную информацию об использовании профилировочных библиотек и о синтаксисе процедур MPE можно найти в документации по МРЕ [3, 1] а также в страницах справочного руководства (man pages).

    Для использования отладчиков и профилировочных библиотек необходимо указывать специальные опции при конфигурации пакета MPICH, которые можно найти в п. 3.5 Руководства системного программиста по ОПО СК “МВС-1000М” [8].

    5.3 Аргументы командной строки для mpirun

    Команда mpirun предоставляет программисту некоторые возможности для облегчения использования отладчика со своей программой.

    Команда

    mpirun -dbx -np 2 program

    начинает выполнение программы на двух машинах, запуская локальную копию программы в отладчике dbx. Опция -gdb позволяет использовать в качестве gdb отладчика, а опция -xxgdb запускает программу в Х Window интерфейсе для gdb – xxgbd.

    5.4 Аргументы MPI для программ пользователя

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

    mpedbg

    при возникновении ошибки в программе пользователя, запускает xterm, присоединенный к процессу, вызвавшему ошибку. MPICH должен быть сконфигурирован с опцией -mpedbg. Данный аргумент работает не на всех системах;

    mpiversion

    печатает версию MPICH и аргументы, использованные при его конфигурировании;

    -mpichdebug

    генерирует детальную информацию по каждой производимой MPICH операции;

    -mpiqueue

    описывает состояние очередей вызова MPI_Finalize. Может быть использован для поиска “потерянных” сообщений.

    Данные аргументы указываются программе пользователя, а не команде mpirun. Например,

    mpirun -np 2 a.out -mpichdebug

    Приложение A

    (справочное)

    Примеры программ с использованием функций MPI

    В данном приложении приведены фрагменты MPI-программ (схематичные примеры) и ряд примеров прикладных программ, использующих MPI.

    Перечень прикладных программ представлен в Таблице 2.

    Таблица 2

    имя файла

    язык

    комментарий

    параметр -np

    fpi.f

    Fortran 77

    Вычисляет число pi, интегрируя f (x) =4 / (1+x2).

    np>=1

    ctest.c

    C

    Пример измерения латентности для вызовов процедур MPI.

    np=2

    cart.c

    C

    Тест виртуальной топологии

    np>1

    tester.c

    C

    Тест обработки ошибок

    np>=1

    srtest.c

    C

    Тест пересылки сообщений

    np>1

    ping_pong.f

    Fortran 77

    Измеряет время передачи сообщений между двумя процессами

    np=2

    mpitest.c

    C

    Тест эффективности основных операций MPI

    np>=4

    cpilog.c

    C

    Программа с использованием профилировочных библиотек

    np>2

    А1 Схематичные примеры MPI-программ

    А1.1 Схематичный пример инициализации параллельной части программы, определение кол-ва процессов в группе MPI_COMM_WORLD, где каждый процесс печатает размер группы и свой номер.

    main(int argc, char **argv)

    {

    int me, size;

    . . .

    MPI_Init (&argc, &argv);

    MPI_Comm_rank (MPI_COMM_WORLD, &me);

    MPI_Comm_size (MPI_COMM_WORLD, &size);

    (void)printf («Process %d size %dn», me, size);

    . . .

    MPI_Finalize();

    }

    А1.2 Схематичный пример программы, где главный процесс рассылает сообщения остальным процессам, а рабочие процессы группы принимают данные и выводят сообщение об этом.

    #include «mpi.h»

    main (argc, argv)

    int argc;

    char **argv;

    {

    char message[20];

    int myrank;

    MPI_Status status;

    MPI_Init (&argc, &argv);

    MPI_Comm_rank (MPI_COMM_WORLD, &myrank);

    if (myrank==0) /* код для процесса 0*/

    {

    strcpy (message, «Hello, there»);

    MPI_Send(messege, strlen(messege), MPI_CAHR, 1, 99, MPI_COMM_WORLD);

    }

    else /* код для процесса 1 */

    {

    MPI_Recv (message, 20, MPI_CHAR, 0, 99, MPI_COMM_WORLD, &status);

    printf («receiveds :%s:n», message);

    }

    MPI_Finalize();

    }

    А1.3 Схематичный пример программы, демонстрирующий обмен сообщениями между процессами группы с помощью процедур MPI_Send и MPI_Recv

    main(int argc, char **argv)

    {

    int me, size;

    int SOME_TAG=0;

    MPI_Status status;

    . . .

    MPI_Init (&argc, &argv);

    MPI_Comm_rank (MPI_COMM_WORLD, &me); /* local */

    MPI_Comm-size (MPI_COMM_WORLD, &size); /* local */

    if (me % 2)==0)

    {

    /* Посылает процессам, пока не дойдет до последнего */

    if ((me+1) < size)

    MPI_Send (…, me+1, SOME_TAG, MPI_COMM_WORLD);

    }

    else

    MPI_Recv (…, me-1, SOME_TAG, MPI_COMM_WORLD, &status);

    . . .

    MPI_Finalize();

    }

    А2 Тест виртуальных топологий (cart.c)

    Это программа на С, генерирующая виртуальную топологию. При компиляции этого примера необходимо подключить (указать путь) файл test.h, который входит в стандартные примеры MPICH и находится в директории /common/mpich/examples/test/topol

    #include «mpi.h»

    #include

    #include «test.h»

    #define NUM_DIMS 2

    int main( int argc, char **argv )

    {

    int rank, size, i;

    int errors=0;

    int dims[NUM_DIMS];

    int periods[NUM_DIMS];

    int coords[NUM_DIMS];

    int new_coords[NUM_DIMS];

    int reorder = 1;

    MPI_Comm comm_temp, comm_cart, new_comm;

    int topo_status;

    int ndims;

    int new_rank;

    int remain_dims[NUM_DIMS];

    int newnewrank;

    MPI_Init( &argc, &argv );

    MPI_Comm_rank( MPI_COMM_WORLD, &rank );

    MPI_Comm_size( MPI_COMM_WORLD, &size );

    /* Обнуление массива сетки и создание топологии */

    for(i=0;i

    MPI_Dims_create ( size, NUM_DIMS, dims );

    /* Создание нового коммуникатора для топологии */

    MPI_Cart_create ( MPI_COMM_WORLD, 2, dims, periods, reorder, &comm_temp );

    MPI_Comm_dup ( comm_temp, &comm_cart );

    /* Определение состояния нового коммуникатора */

    MPI_Topo_test ( comm_cart, &topo_status );

    if (topo_status != MPI_CART) errors++;

    /* Определение кол-ва измерений сетки в топологии */

    MPI_Cartdim_get( comm_cart, &ndims );

    if ( ndims != NUM_DIMS ) errors++;

    /* Проверка корректности полученной топологии */

    for(i=0;i

    MPI_Cart_get ( comm_cart, NUM_DIMS, dims, periods, coords );

    /* Проверка соответствия координат процесса в сетке топологии номеру процесса */

    MPI_Cart_rank ( comm_cart, coords, &new_rank );

    if ( new_rank != rank ) errors++;

    /* Проверка соответствия номера процесса координатам процесса в сетке топологии */

    MPI_Cart_coords ( comm_cart, rank, NUM_DIMS, new_coords );

    for (i=0;i

    if ( coords[i] != new_coords[i] )

    errors++;

    /* Сдвиг в каждом измерении сетки топологии и проверка ее работы*/

    for (i=0;i

    int source, dest;

    MPI_Cart_shift(comm_cart, i, 1, &source, &dest);

    #ifdef VERBOSE

    printf («[%d] Shifting %d in the %d dimensionn»,rank,1,i);

    printf («[%d] source = %d dest = %dn»,rank,source,dest);

    #endif

    }

    /* Выделение подгрупп коммуникатора для подсеток топологии */

    remain_dims[0] = 0;

    for (i=1; i

    MPI_Cart_sub ( comm_cart, remain_dims, &new_comm );

    /* Определение статуса нового коммуникатора */

    MPI_Topo_test ( new_comm, &topo_status );

    if (topo_status != MPI_CART) errors++;

    /* Определение кол-ва измерений сетки в топологии */

    MPI_Cartdim_get( new_comm, &ndims );

    if ( ndims != NUM_DIMS-1 ) errors++;

    /* Проверка корректности полученной топологии */

    for(i=0;i

    MPI_Cart_get ( new_comm, ndims, dims, periods, coords );

    /* Проверка соответствия координат процесса в сетке топологии номеру процесса */

    MPI_Comm_rank ( new_comm, &newnewrank );

    MPI_Cart_rank ( new_comm, coords, &new_rank );

    if ( new_rank != newnewrank ) errors++;

    /* Проверка соответствия номера процесса координатам процесса в сетке топологии */

    MPI_Cart_coords ( new_comm, new_rank, NUM_DIMS -1, new_coords );

    for (i=0;i

    if ( coords[i] != new_coords[i] )

    errors++;

    /* Завершение программы */

    MPI_Comm_free( &new_comm );

    MPI_Comm_free( &comm_temp );

    MPI_Comm_free( &comm_cart );

    Test_Waitforall( );

    if (errors) printf( «[%d] done with %d ERRORS!n», rank,errors );

    MPI_Finalize();

    return 0;

    }

    Вывод результата работы программы для np=2

    call_batch: calling batch

    All processes completed test

    Вывод результата работы программы для np=32

    call_batch: calling batch

    All processes completed test

    А3 Тест пересылки сообщений (srtest.c)

    Эта программа выдает сообщения о коммуникациях в группе.

    #include «mpi.h»

    #include

    #define BUFLEN 512

    int main(argc,argv)

    int argc;

    char *argv[];

    {

    int i, myid, numprocs, next, rc, namelen;

    char buffer[BUFLEN], processor_name[MPI_MAX_PROCESSOR_NAME];

    MPI_Status status;

    MPI_Init(&argc,&argv);

    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

    MPI_Comm_rank(MPI_COMM_WORLD,&myid);

    MPI_Get_processor_name(processor_name,&namelen);

    fprintf(stderr,»Process %d on %sn», myid, processor_name);

    strcpy(buffer,»hello there»);

    if (myid == numprocs-1)

    next = 0;

    else

    next = myid+1;

    if (myid == 0)

    {

    printf(«%d sending ‘%s’ n»,myid,buffer);

    MPI_Send(buffer, strlen(buffer)+1, MPI_CHAR, next, 99, MPI_COMM_WORLD);

    printf(«%d receiving n»,myid);

    MPI_Recv(buffer, BUFLEN, MPI_CHAR, MPI_ANY_SOURCE, 99, MPI_COMM_WORLD,

    &status);

    printf(«%d received ‘%s’ n»,myid,buffer);

    }

    else

    {

    printf(«%d receiving n»,myid);

    MPI_Recv(buffer, BUFLEN, MPI_CHAR, MPI_ANY_SOURCE, 99, MPI_COMM_WORLD,

    &status);

    printf(«%d received ‘%s’ n»,myid,buffer);

    MPI_Send(buffer, strlen(buffer)+1, MPI_CHAR, next, 99, MPI_COMM_WORLD);

    printf(«%d sent ‘%s’ n»,myid,buffer);

    }

    MPI_Barrier(MPI_COMM_WORLD);

    MPI_Finalize();

    }

    Вывод результата работы программы для np=2

    call_batch: calling batch

    1 receiving

    0 sending ‘hello there’

    0 receiving

    0 received ‘hello there’

    1 received ‘hello there’

    1 sent ‘hello there’

    Вывод результата работы программы для np=32

    call_batch: calling batch

    20 receiving

    30 receiving

    8 receiving

    13 receiving

    10 receiving

    16 receiving

    2 receiving

    4 receiving

    6 receiving

    9 receiving

    7 receiving

    12 receiving

    3 receiving

    14 receiving

    21 receiving

    11 receiving

    17 receiving

    18 receiving

    19 receiving

    5 receiving

    15 receiving

    24 receiving

    22 receiving

    26 receiving

    23 receiving

    28 receiving

    27 receiving

    25 receiving

    29 receiving

    31 receiving

    1 receiving

    0 sending ‘hello there’

    20 received ‘hello there’

    20 sent ‘hello there’

    30 received ‘hello there’

    30 sent ‘hello there’

    8 received ‘hello there’

    8 sent ‘hello there’

    18 received ‘hello there’

    18 sent ‘hello there’

    13 received ‘hello there’

    13 sent ‘hello there’

    10 received ‘hello there’

    10 sent ‘hello there’

    21 received ‘hello there’

    21 sent ‘hello there’

    16 received ‘hello there’

    16 sent ‘hello there’

    4 received ‘hello there’

    4 sent ‘hello there’

    2 received ‘hello there’

    2 sent ‘hello there’

    6 received ‘hello there’

    6 sent ‘hello there’

    12 received ‘hello there’

    12 sent ‘hello there’

    11 received ‘hello there’

    11 sent ‘hello there’

    9 received ‘hello there’

    9 sent ‘hello there’

    14 received ‘hello there’

    14 sent ‘hello there’

    19 received ‘hello there’

    19 sent ‘hello there’

    22 received ‘hello there’

    22 sent ‘hello there’

    28 received ‘hello there’

    28 sent ‘hello there’

    25 received ‘hello there’

    25 sent ‘hello there’

    27 received ‘hello there’

    27 sent ‘hello there’

    31 received ‘hello there’

    31 sent ‘hello there’

    3 received ‘hello there’

    3 sent ‘hello there’

    7 received ‘hello there’

    7 sent ‘hello there’

    17 received ‘hello there’

    17 sent ‘hello there’

    26 received ‘hello there’

    26 sent ‘hello there’

    23 received ‘hello there’

    23 sent ‘hello there’

    24 received ‘hello there’

    24 sent ‘hello there’

    29 received ‘hello there’

    29 sent ‘hello there’

    5 received ‘hello there’

    5 sent ‘hello there’

    15 received ‘hello there’

    15 sent ‘hello there’

    0 receiving

    0 received ‘hello there’

    1 received ‘hello there’

    1 sent ‘hello there’

    А4 Вычисление числа Pi ( fpi.f )

    Это пример программы на Fortran 77, который вычисляет число pi интегрируя f(x) = 4/(1 + x2).

    Каждый процесс:

    • получает число интервалов, используемых в приближении;

    • вычисляет в них интегралы;

    • синхронизирует для общего суммирования.

    Программа выдает результат вычислений, погрешность и время вычисления.

    program main

    include ‘mpif.h’

    double precision PI25DT

    parameter (PI25DT = 3.141592653589793238462643d0)

    double precision mypi, pi, h, sum, x, f, a

    integer n, myid, numprocs, i, rc

    c Интегрируемая функция

    f(a) = 4.d0 / (1.d0 + a*a)

    call MPI_INIT( ierr )

    call MPI_COMM_RANK( MPI_COMM_WORLD, myid, ierr )

    call MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ierr )

    print *, «Process «, myid, » of «, numprocs, » is alive»

    sizetype = 1

    sumtype = 2

    n = 0

    10 if ( myid .eq. 0 ) then

    write(6,98)

    98 format(‘Enter the number of intervals: (0 quits)’)

    read(5,99) n

    99 format(i10)

    c if ( n .eq. 0 ) then

    c n = 100

    c else

    c n = 0

    c endif

    print *, «got input n =», n

    endif

    call MPI_BCAST(n,1,MPI_INTEGER,0,MPI_COMM_WORLD,ierr)

    c проверка принятия сообщения

    if ( n .le. 0 ) goto 30

    c вычисление размера интервала

    h = 1.0d0/n

    sum = 0.0d0

    do 20 i = myid+1, n, numprocs

    x = h * (dble(i) — 0.5d0)

    sum = sum + f(x)

    20 continue

    mypi = h * sum

    c прием всех частичных сумм

    call MPI_REDUCE(mypi,pi,1,MPI_DOUBLE_PRECISION,MPI_SUM,0,

    $ MPI_COMM_WORLD,ierr)

    c печать ответа в 0-й ветви.

    if (myid .eq. 0) then

    write(6, 97) pi, abs(pi — PI25DT)

    97 format(‘ pi is approximately: ‘, F18.16,

    + ‘ Error is: ‘, F18.16)

    endif

    goto 10

    30 call MPI_FINALIZE(rc)

    stop

    end

    Вывод результата работы программы для np=2

    call_batch: calling batch

    Process 1 of 2 is alive

    Process 0 of 2 is alive

    Enter the number of intervals: (0 quits)

    Вывод результата работы программы для np=32

    call_batch: calling batch

    Process 29 of 32 is alive

    Process 5 of 32 is alive

    Process 2 of 32 is alive

    Process 11 of 32 is alive

    Process 4 of 32 is alive

    Process 3 of 32 is alive

    Process 6 of 32 is alive

    Process 7 of 32 is alive

    Process 8 of 32 is alive

    Process 13 of 32 is alive

    Process 9 of 32 is alive

    Process 10 of 32 is alive

    Process 12 of 32 is alive

    Process 15 of 32 is alive

    Process 14 of 32 is alive

    Process 16 of 32 is alive

    Process 19 of 32 is alive

    Process 17 of 32 is alive

    Process 18 of 32 is alive

    Process 20 of 32 is alive

    Process 21 of 32 is alive

    Process 22 of 32 is alive

    Process 23 of 32 is alive

    Process 24 of 32 is alive

    Process 26 of 32 is alive

    Process 25 of 32 is alive

    Process 27 of 32 is alive

    Process 28 of 32 is alive

    Process 30 of 32 is alive

    Process 31 of 32 is alive

    Process 1 of 32 is alive

    Process 0 of 32 is alive

    Enter the number of intervals: (0 quits)

    A5 Измерение латентности вызовов процедур MPI (ctest.c)

    Это пример программы на C , где латентность вызовов процедур MPI находится путем измерения разницы между временем вызова процедуры и результатами времени в эталонном тесте Dongarra.

    #include «mpi.h»

    #include

    #include

    #define MAX_TIMES 16386

    static double times[MAX_TIMES];

    int main( argc, argv )

    int argc;

    char **argv;

    {

    int i;

    int ntest = MAX_TIMES;

    double minsep, maxsep, avesep, sep, sd, deltasep, mult;

    int citoutput = 1, nmatch;

    MPI_Init( &argc, &argv );

    /* Установление счетчиков времени */

    for (i=0; i

    times[i] = MPI_Wtime();

    /* Получение начальных данных. При этом не измеряется время неудачных обращений к кэшу команды, занимающее дополнительное время */

    for (i=0; i

    times[i] = MPI_Wtime();

    /* Просмотр вариантов */

    minsep = 1.0e6;

    maxsep = 0.0;

    avesep = 0.0;

    for (i=1; i

    sep = times[i] — times[i-1];

    if (sep < minsep) minsep = sep;

    if (sep > maxsep) maxsep = sep;

    avesep += sep;

    }

    avesep /= (ntest-1);

    /* C Вычисление среднеквадратичного отклонение разниц относительно устойчивым способом */

    sd = 0.0;

    for (i=1; i

    sep = times[i] — times[i-1];

    sd += (sep — avesep) * (sep — avesep);

    }

    /* Масштаб в мкс */

    sd *= 1.0e+12;

    sd = sqrt(sd) / (ntest — 2);

    /* Интересный вопрос — — все ли (или большинство) разниц являются множителями minsep. Сначала находят deltasep (время между первым и вторым различными измерениями.

    */

    deltasep = maxsep;

    for (i=1; i

    sep = times[i] — times[i-1];

    if (sep > minsep && sep < deltasep) deltasep = sep;

    }

    deltasep -= minsep;

    /* Далее находят кол-во разниц, являющихся множителями deltasep */

    nmatch = 0;

    for (i=1; i

    sep = times[i] — times[i-1];

    mult = (sep — minsep) / deltasep;

    if (fabs( mult — (int)mult) < 0.05) nmatch++;

    }

    /* Печать результатов */

    printf( «#Variance in clock:n

    #Minimum time between calls: %6.2f usecn

    #Maximum time between calls: %6.2f usecn

    #Average time between calls: %6.2f usecn

    #Standard deviation: %12.3en»,

    minsep * 1.e6, maxsep * 1.e6, avesep * 1.e6, sd );

    if (nmatch > ntest/4) {

    printf( «#Apparent resolution of clock is: %6.2f usecn»,

    deltasep * 1.0e6 );

    }

    printf(

    «# This program should be run multiple times for better understandingn» );

    /* Print C.It graphics stuff if requested */

    if (citoutput) {

    for (i=1; i

    sep = times[i] — times[i-1];

    printf( «%fn», sep * 1.0e6 );

    }

    printf( «histnwaitnnew pagen» );

    }

    MPI_Finalize();

    return 0;

    }

    Вывод результата работы программы для np=2

    call_batch: calling batch

    #Variance in clock:

    #Variance in clock:

    #Minimum time between calls: 0.00 usec

    #Maximum time between calls: 978.11 usec

    #Average time between calls: 1.37 usec

    #Standard deviation: 2.856e-01

    #Apparent resolution of clock is: 0.95 usec

    # This program should be run multiple times for better understanding

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    …………………

    975.966454

    2.026558

    …………………

    0.000000

    0.000000

    …………………

    Вывод результата работы программы для np=32

    call_batch: calling batch

    #Variance in clock:

    #Minimum time between calls: 0.00 usec

    #Maximum time between calls: 977.99 usec

    #Average time between calls: 0.84 usec

    #Standard deviation: 2.230e-01

    #Apparent resolution of clock is: 0.95 usec

    # This program should be run multiple times for better understanding

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    0.000000

    …………………

    0.000000

    #Minimum time between calls: 0.00 usec

    #Maximum time between calls: 978.11 usec

    #Average time between calls: 1.01 usec

    #Standard deviation: 2.457e-01

    #Apparent resolution of clock is: 0.95 usec

    # This program should be run multiple times for better understanding

    0.000000

    0.000000

    0.000000

    …………………

    A6 Тест обработки ошибок (testerr.c)

    Эта программа показывает работу процедур для обработки ошибок.

    #include

    #include

    void Test_Send( void );

    void Test_Recv( void );

    void Test_Datatype( void );

    void Test_Errors_warn( MPI_Comm *comm, int *code, … )

    {

    char buf[MPI_MAX_ERROR_STRING+1];

    int myid, result_len;

    static int in_handler = 0;

    if (in_handler) return;

    in_handler = 1;

    /* Преобразование кода ошибки в сообщение и печать */

    MPI_Error_string( *code, buf, &result_len );

    printf( «%sn», buf );

    in_handler = 0;

    }

    static int errcount = 0;

    void Test_Failed( char * msg )

    {

    printf( «FAILED: %sn», msg );

    errcount++;

    }

    void Test_Passed( char * msg )

    {

    printf( «Passed: %sn», msg );

    }

    int main( int argc, char *argv[] )

    {

    MPI_Errhandler TEST_ERRORS_WARN;

    MPI_Init( &argc, &argv );

    MPI_Errhandler_create( Test_Errors_warn, &TEST_ERRORS_WARN );

    MPI_Errhandler_set(MPI_COMM_WORLD, TEST_ERRORS_WARN);

    Test_Send();

    Test_Recv();

    Test_Datatype();

    MPI_Finalize();

    return 0;

    }

    void Test_Send( void )

    {

    int buffer[100];

    int dest;

    MPI_Datatype bogus_type = MPI_DATATYPE_NULL;

    MPI_Status status;

    int myrank, size;

    int large_tag, flag, small_tag;

    int *tag_ubp;

    MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

    MPI_Comm_size( MPI_COMM_WORLD, &size );

    dest = size — 1;

    if (MPI_Send(buffer, 20, MPI_INT, dest,

    1, MPI_COMM_NULL) == MPI_SUCCESS){

    Test_Failed(«NULL Communicator Test»);

    }

    else

    Test_Passed(«NULL Communicator Test»);

    if (MPI_Send(buffer, -1, MPI_INT, dest,

    1, MPI_COMM_WORLD) == MPI_SUCCESS){

    Test_Failed(«Invalid Count Test»);

    }

    else

    Test_Passed(«Invalid Count Test»);

    if (MPI_Send(buffer, 20, bogus_type, dest,

    1, MPI_COMM_WORLD) == MPI_SUCCESS){

    Test_Failed(«Invalid Type Test»);

    }

    else

    Test_Passed(«Invalid Type Test»);

    small_tag = -1;

    if (small_tag == MPI_ANY_TAG) small_tag = -2;

    if (MPI_Send(buffer, 20, MPI_INT, dest,

    small_tag, MPI_COMM_WORLD) == MPI_SUCCESS) {

    Test_Failed(«Invalid Tag Test»);

    }

    else

    Test_Passed(«Invalid Tag Test»);

    /* Тег сообщения слишком велик */

    MPI_Attr_get( MPI_COMM_WORLD, MPI_TAG_UB, (void **)&tag_ubp, &flag );

    if (!flag) Test_Failed(«Could not get tag ub!» );

    large_tag = *tag_ubp + 1;

    if (large_tag > *tag_ubp) {

    if (MPI_Send(buffer, 20, MPI_INT, dest,

    -1, MPI_COMM_WORLD) == MPI_SUCCESS) {

    Test_Failed(«Invalid Tag Test»);

    }

    else

    Test_Passed(«Invalid Tag Test»);

    }

    if (MPI_Send(buffer, 20, MPI_INT, 300,

    1, MPI_COMM_WORLD) == MPI_SUCCESS) {

    Test_Failed(«Invalid Destination Test»);

    }

    else

    Test_Passed(«Invalid Destination Test»);

    if (MPI_Send((void *)0, 10, MPI_INT, dest,

    1, MPI_COMM_WORLD) == MPI_SUCCESS){

    Test_Failed(«Invalid Buffer Test (send)»);

    }

    else

    Test_Passed(«Invalid Buffer Test (send)»);

    }

    void Test_Recv( void )

    {

    }

    void Test_Datatype( void )

    {

    }

    #ifdef FOO

    void

    ReceiverTest3()

    {

    int buffer[20];

    MPI_Datatype bogus_type = MPI_DATATYPE_NULL;

    MPI_Status status;

    int myrank;

    int *tag_ubp;

    int large_tag, flag, small_tag;

    MPI_Comm_rank( MPI_COMM_WORLD, &myrank );

    if (myrank == 0) {

    fprintf( stderr,

    «There should be eight error messages about invalid communicatorn

    count argument, datatype argument, tag, rank, buffer send and buffer recvn» );

    }

    /* Тест посылки сообщений может не выдавать ошибок, пока он не запущен */

    if (MPI_Recv((void *)0, 10, MPI_INT, src,

    15, MPI_COMM_WORLD, &status) == MPI_SUCCESS){

    Test_Failed(«Invalid Buffer Test (recv)»);

    }

    else

    Test_Passed(«Invalid Buffer Test (recv)»);

    /* Прием посылаеммого сообщения */

    { int flag, ibuf[10];

    MPI_Iprobe( src, 15, MPI_COMM_WORLD, &flag, &status );

    if (flag)

    MPI_Recv( ibuf, 10, MPI_INT, src, 15, MPI_COMM_WORLD, &status );

    }

    return;

    #endif

    Вывод результата работы программы для np=2

    call_batch: calling batch

    Null communicator

    Null communicator

    Passed: NULL Communicator Test

    Invalid count argument is -1

    Passed: Invalid Count Test

    Datatype is MPI_TYPE_NULL

    Passed: Invalid Type Test

    Invalid message tag -2

    Passed: Invalid Tag Test

    Invalid message tag -1

    Passed: Invalid Tag Test

    Invalid rank 300

    Passed: Invalid Destination Test

    Invalid buffer pointer

    Passed: Invalid Buffer Test (send)

    Passed: NULL Communicator Test

    Invalid count argument is -1

    Passed: Invalid Count Test

    Datatype is MPI_TYPE_NULL

    Passed: Invalid Type Test

    Invalid message tag -2

    Passed: Invalid Tag Test

    Вывод результата работы программы для np=32

    call_batch: calling batch

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Null communicator

    Passed: NULL Communicator Test

    Invalid count argument is -1

    Passed: Invalid Count Test

    Datatype is MPI_TYPE_NULL

    Passed: Invalid Type Test

    Invalid message tag -2

    Passed: Invalid Tag Test

    Invalid message tag –1

    Passed: Invalid Tag Test

    Invalid rank 300

    Passed: Invalid Destination Test

    Invalid buffer pointer

    Passed: Invalid Buffer Test (send)

    Null communicator

    Passed: NULL Communicator Test

    Invalid count argument is -1

    Passed: Invalid Count Test

    Datatype is MPI_TYPE_NULL

    Passed: Invalid Type Test

    Invalid message tag -2

    Passed: Invalid Tag Test

    Invalid message tag -1

    Passed: Invalid Tag Test

    Invalid rank 300

    Passed: Invalid Destination Test

    Invalid buffer pointer

    ……………………………………………………

    A7 Обмен сообщениями (ping_pong.f)

    Этот пример измеряет скорость пересылки сообщений.

    PROGRAM PingPong

    INCLUDE ‘mpif.h’

    integer p

    integer my_rank

    integer test

    integer min_size

    integer max_size

    integer incr

    integer size

    parameter (min_size = 0, max_size = 10000, incr = 200)

    parameter (size = 80000)

    double precision x(0:size)

    integer pass

    integer status(MPI_STATUS_SIZE)

    integer ierr

    integer i

    double precision wtime_overhead

    double precision start, finish,timex

    double precision raw_time, cumulativ,y,tmin

    double precision dtime(0:127,0:127),delta,tmax,tavg

    double precision tmax2,tmin2,tavg2

    integer comm, count(0:127)

    integer MAX_ORDER, MAX

    parameter (MAX_ORDER = 1000, MAX = 8)

    character*23 nodes(0:127),dummy

    character*4 pname(0:127), myname

    C

    call MPI_INIT( ierr)

    call MPI_COMM_SIZE(MPI_COMM_WORLD, p, ierr )

    call MPI_COMM_RANK(MPI_COMM_WORLD, my_rank, ierr )

    call MPI_COMM_DUP(MPI_COMM_WORLD, comm, ierr )

    call MPI_GET_PROCESSOR_NAME(myname,len,ierr)

    call MPI_GATHER(myname,4,MPI_CHARACTER,pname,4,

    + MPI_CHARACTER,0,MPI_COMM_WORLD,ierr)

    call MPI_BCAST(pname,512,MPI_CHARACTER,0,MPI_COMM_WORLD,ierr)

    if(my_rank.eq.0) print*,»Starting…»

    C

    C

    y=4.D0*DATAN(1.D0)

    do 9 i=1,size

    x(i)=y

    9 continue

    noff=mod(p,2)

    C mynoff=mod(myrank,2)

    if(noff.eq.1) then

    print*, «Error: odd number of processes…»

    stop

    endif

    print 181, my_rank

    tavg=0.D0

    tmin=99.D99

    tmax=0.D0

    delta=3.0D-004

    C nloops=20

    nloops=200

    mynoff=mod(my_rank,2)

    do 101 nnn=0,1

    do 100 i=1,p-1,2

    ndest=my_rank+i

    nsend=my_rank-i

    if(ndest.ge.p) ndest=mod(ndest,p)

    if(nsend.lt.0) nsend=nsend+p

    call MPI_BARRIER(comm, ierr)

    if (mynoff.eq.nnn) then

    timex=0.D0

    do 20 k=1,nloops

    start = MPI_WTIME()

    call MPI_SEND(x, size,MPI_DOUBLE_PRECISION,

    + ndest,0,comm,ierr)

    finish = MPI_WTIME()

    timex=timex+(finish-start)

    11 continue

    20 continue

    ttime=timex/dble(nloops)

    tavg=tavg+ttime

    if(ttime.gt.tmax) tmax=ttime

    if(ttime.lt.tmin) tmin=ttime

    else

    nflag=0

    do 22 k=1,nloops

    call MPI_RECV(x,size,MPI_DOUBLE_PRECISION,nsend,

    + 0,comm,status, ierr)

    do 21 k1=1,size

    if (DABS(x(k1)-y).gt.1D-16) then

    print 179, x(k1)

    print 182, nsend,comm,status,ierr

    print 180, my_rank, k1, k1

    print 178, y

    nflag=1

    endif

    21 continue

    22 continue

    if(nflag.eq.1)print 177, pname(nsend),

    + pname(my_rank),nsend,my_rank

    endif

    100 continue

    101 continue

    call MPI_REDUCE(tmin, tmin2, 1, MPI_DOUBLE_PRECISION, MPI_MIN, 0,

    + MPI_COMM_WORLD, ierr)

    call MPI_REDUCE(tmax, tmax2, 1, MPI_DOUBLE_PRECISION, MPI_MAX, 0,

    + MPI_COMM_WORLD, ierr)

    call MPI_REDUCE(tavg, tavg2, 1, MPI_DOUBLE_PRECISION, MPI_SUM, 0,

    + MPI_COMM_WORLD, ierr)

    if (my_rank.eq.0) then

    print*,»Max time: «,tmax2

    print*,»Min time: «,tmin2

    print*,»Avg time: «,tavg2/dble(p**2/4)

    endif

    176 format(«Excessive Message Delay?: «,A,» -> «,A,2x,I3,2x,I3,

    + 2x,D17.10)

    177 format(«Message Error: «,A,» -> «,A,2x,I3,2x,I3,2x,D17.10,2x,

    + D17.10)

    181 format(2x,»my rank «, I5)

    178 format(10x,»Sent: «,Z17)

    179 format(10x,»Recv: «,Z17)

    182 format(5x,»Message from»,2x,I3,3x,»comm»,2x,I4,3x,»status»,

    + 2x,Z8,3x,»ierr»,2x,I4)

    180 format(12x,»my rank «,I4,3x,»index «,I8,3x,»in hex»,Z8)

    call MPI_FINALIZE(ierr)

    end

    Вывод результата работы программы для np=2

    call_batch: calling batch

    Starting…

    my rank 1

    my rank 0

    Max time: 3.239690558984876E-003

    Min time: 3.174345009028912E-003

    Avg time: 6.414035568013787E-003

    A8 Тест эффективности основных операций MPI ( mpitest)

    #include

    #include

    #include

    #define Wtime MPI_Wtime

    #define MEGABYTE (1024*1024)

    #define BUFFER_SIZE 1024

    #define TAG1 17

    #define MASTER_RANK 0

    #define MAX_REQUESTS 16

    #define MILLION 1000000.0

    static int NTIMES = 10000;

    static int NTIMES_PREC = 100000;

    #define NTIMES_TIMING NTIMES_PREC

    #define NTIMES_BARRIER NTIMES_PREC

    #define NTIMES_LATENCY NTIMES_PREC

    #define TEST_SENDRECV 0

    #define MASTER if(i_am_the_master)

    int nproc,myid;

    int i_am_the_master = 0;

    int buf[BUFFER_SIZE],obuf[BUFFER_SIZE];

    void Test_MPI_Routines(MPI_Comm comm);

    int main(int argc,char *argv[]) {

    int i;

    double t_start,t_end;

    MPI_Init(&argc,&argv);

    MPI_Comm_size(MPI_COMM_WORLD,&nproc);

    MPI_Comm_rank(MPI_COMM_WORLD,&myid);

    i_am_the_master = (myid == MASTER_RANK);

    t_start = Wtime();

    MASTER printf(«MPItest/C 1.0: running %d processes…n»,nproc);

    if(nproc < 4) {

    MASTER printf(«MPItest expects at least 4 processesn»);

    MPI_Finalize();

    exit(0);

    }

    MASTER /* читает параметры коммандной строки */

    for(i = 1; i < argc; i ++)

    switch(argv[i][0]) {

    case ‘T’: NTIMES = atoi(argv[i]+1); break; case ‘t’: NTIMES_PREC = atoi(argv[i]+1); break; default: fprintf(stderr,»WARNING: unrecognized option: %sn»,argv[i]);

    break;

    }

    MPI_Bcast(&NTIMES,1,MPI_INT,MASTER_RANK,MPI_COMM_WORLD);

    MPI_Bcast(&NTIMES_PREC,1,MPI_INT,MASTER_RANK,MPI_COMM_WORLD);

    MASTER printf(«Testing basic MPI routines.n»);

    Test_MPI_Routines(MPI_COMM_WORLD);

    t_end = Wtime();

    MASTER printf(«MPItest/C complete in %g sec.n»,t_end-t_start);

    MPI_Finalize();

    return 0;

    }

    void Test_MPI_Routines(MPI_Comm comm) {

    int nproc,myid,i;

    int i_am_the_master = 0;

    double t, t1, t2, tt, dt, lat, tb;

    MPI_Status status, statuses[MAX_REQUESTS];

    MPI_Request request, requests[MAX_REQUESTS];

    MPI_Comm_size(MPI_COMM_WORLD,&nproc);

    MPI_Comm_rank(MPI_COMM_WORLD,&myid);

    i_am_the_master = (myid == MASTER_RANK);

    MPI_Barrier(comm);

    /* Инициализация буффера */

    for(i = 0; i < BUFFER_SIZE; i ++) buf[i] = myid;

    /* —— Измерение времени между вызовами MPI_Wtime() —— */

    MASTER printf(«** Timing for MPI operations in microseconds ***n»);

    dt = 0;

    MASTER for(i = 0; i < NTIMES_TIMING; i ++)

    {

    t1 = Wtime();

    t2 = Wtime();

    dt += (t2 — t1);

    }

    dt /= (double) NTIMES_TIMING;

    t = dt;

    t *= MILLION;

    MASTER printf(«%gttTimingn»,t);

    fflush(stdout);

    /* ———— Проверка барьера синхронизации ———- */

    tb = 0;

    for(i = 0; i < NTIMES_BARRIER; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Barrier(comm);

    t2 = Wtime();

    tb += (t2 — t1 — dt);

    }

    tb /= (double) NTIMES_BARRIER;

    t = tb;

    t *= MILLION;

    MASTER printf(«%gttBarriern»,t);

    fflush(stdout);

    /* —— Объединение данных от всех ветвей с помощью MPI_Allreduce —— */

    t = 0;

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Allreduce(buf,obuf,1,MPI_INT,MPI_SUM,comm);

    MPI_Barrier(comm);

    t2 = Wtime();

    t += (t2 — t1 — dt — tb);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttGlobal sum (Allreduce)n»,t);

    fflush(stdout);

    /* ——-Объединение данных от всех ветвей с помощью MPI_Allreduce —— */

    t = 0;

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Allreduce(buf,obuf,10,MPI_INT,MPI_SUM,comm);

    MPI_Barrier(comm);

    t2 = Wtime();

    t += (t2 — t1 — dt — tb);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gtt10 global sums (Allreduce)n»,t);

    fflush(stdout);

    /* ——-Объединение данных от всех ветвей с помощью MPI_Allreduce —— */

    t = 0;

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Reduce(buf,obuf,1,MPI_INT,MPI_SUM,MASTER_RANK,comm);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttGlobal sum (Reduce)n»,t);

    fflush(stdout);

    /* ——-Объединение данных от всех ветвей с помощью MPI_Allreduce —— */

    t = 0;

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Reduce(buf,obuf,10,MPI_INT,MPI_SUM,MASTER_RANK,comm);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gtt10 Global sums (Reduce)n»,t);

    fflush(stdout);

    /*Использование процедуры передачи сообщения от главного процесса остальным*/

    t = 0;

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Bcast(buf,1,MPI_INT,MASTER_RANK,comm);

    MPI_Barrier(comm);

    t2 = Wtime();

    t += (t2 — t1 — dt — tb);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttBroadcastn»,t);

    fflush(stdout);

    /* Использование процедуры сбора сообщений главным процессом от остальных */

    t = 0;

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Barrier(comm);

    t1 = Wtime();

    MPI_Gather(buf,1,MPI_INT,obuf,1,MPI_INT,MASTER_RANK,comm);

    MPI_Barrier(comm);

    t2 = Wtime();

    t += (t2 — t1 — dt — tb);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttGathern»,t);

    fflush(stdout);

    /* Использование неблокирующей посылки сообщений и ожидания ее конца MPI_Wait */

    t = 0;

    if(i_am_the_master)

    for(i = 0; i < NTIMES; i ++)

    {

    t1 = Wtime();

    MPI_Isend(buf,10,MPI_INT,1,TAG1,comm,&request);

    MPI_Wait(&request,&status);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    else if(myid == 1)

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Irecv(obuf,10,MPI_INT,MASTER_RANK,TAG1,comm,&request);

    MPI_Wait(&request,&status);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttNon-blocking send with waitn»,t);

    fflush(stdout);

    /* Использование блокирующей посылки сообщений ———- */

    t = 0;

    if(i_am_the_master)

    for(i = 0; i < NTIMES; i ++)

    {

    t1 = Wtime();

    MPI_Send(buf,10,MPI_INT,1,TAG1,comm);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    else if(myid == 1)

    for(i = 0; i < NTIMES; i ++)

    MPI_Recv(obuf,10,MPI_INT,MASTER_RANK,TAG1,comm,&status);

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttBlocking vector sendn»,t);

    fflush(stdout);

    /* ——— Использование одновременной передачи и приема сообщения —- */

    t = 0;

    if(i_am_the_master)

    for(i = 0; i < NTIMES; i ++)

    {

    t1 = Wtime();

    MPI_Sendrecv(buf,1,MPI_INT,1,TAG1,obuf,1, MPI_INT, 1, TAG1, comm,statuses);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    else if(myid == 1)

    for(i = 0; i < NTIMES; i ++)

    MPI_Sendrecv(buf, 1, MPI_INT, MASTER_RANK, TAG1,obuf, 1, MPI_INT, MASTER_RANK, TAG1, comm,statuses);

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttSendRecvn»,t);

    fflush(stdout);

    /* — Использование последовательной передачи и приема сообщения —-*/

    t = 0;

    if(i_am_the_master)

    for(i = 0; i < NTIMES; i ++)

    {

    t1 = Wtime();

    MPI_Send(buf,1,MPI_INT,1,TAG1,comm);

    MPI_Recv(obuf,1,MPI_INT, 1,TAG1,comm,&status);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    else if(myid == 1)

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Recv(obuf,1,MPI_INT,MASTER_RANK,TAG1,comm,&status);

    MPI_Send(buf,1,MPI_INT,MASTER_RANK,TAG1,comm);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gttSend and receiven»,t);

    fflush(stdout);

    /* —-Одновременное выполнение запросов на посылка сообщенияs —- */

    #if 1

    t = 0;

    if(i_am_the_master)

    {

    MPI_Send_init(&buf[0],1,MPI_INT,1,TAG1,comm,&requests[0]);

    MPI_Send_init(&buf[1],1,MPI_INT,1,TAG1,comm,&requests[1]);

    MPI_Send_init(&buf[2],1,MPI_INT,1,TAG1,comm,&requests[2]);

    MPI_Send_init(&buf[3],1,MPI_INT,1,TAG1,comm,&requests[3]);

    MPI_Send_init(&buf[4],1,MPI_INT,1,TAG1,comm,&requests[4]);

    for(i = 0; i < NTIMES; i ++)

    {

    t1 = Wtime();

    MPI_Startall(5,requests);

    MPI_Waitall(5,requests,statuses);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    MPI_Request_free(&requests[0]);

    MPI_Request_free(&requests[1]);

    MPI_Request_free(&requests[2]);

    MPI_Request_free(&requests[3]);

    MPI_Request_free(&requests[4]);

    }

    else if(myid == 1)

    {

    MPI_Recv_init(&obuf[0],1,MPI_INT,0,TAG1,comm,&requests[0]);

    MPI_Recv_init(&obuf[1],1,MPI_INT,0,TAG1,comm,&requests[1]);

    MPI_Recv_init(&obuf[2],1,MPI_INT,0,TAG1,comm,&requests[2]);

    MPI_Recv_init(&obuf[3],1,MPI_INT,0,TAG1,comm,&requests[3]);

    MPI_Recv_init(&obuf[4],1,MPI_INT,0,TAG1,comm,&requests[4]);

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Startall(5,requests);

    MPI_Waitall(5,requests,statuses);

    }

    MPI_Request_free(&requests[0]);

    MPI_Request_free(&requests[1]);

    MPI_Request_free(&requests[2]);

    MPI_Request_free(&requests[3]);

    MPI_Request_free(&requests[4]);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gtt5 sends in onen»,t);

    fflush(stdout);

    #endif

    /* ———— Выполнение посылки сообщения по запросу ———- */

    t = 0;

    if(i_am_the_master)

    for(i = 0; i < NTIMES; i ++)

    {

    t1 = Wtime();

    MPI_Isend(&buf[0],1,MPI_INT,1,TAG1,comm, &requests[0]);

    MPI_Isend(&buf[0],1,MPI_INT,1,TAG1,comm, &requests[1]);

    MPI_Isend(&buf[0],1,MPI_INT,1,TAG1,comm, &requests[2]);

    MPI_Isend(&buf[0],1,MPI_INT,1,TAG1,comm, &requests[3]);

    MPI_Isend(&buf[0],1,MPI_INT,1,TAG1,comm, &requests[4]);

    MPI_Waitall(5,requests,statuses);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    else if(myid == 1)

    for(i = 0; i < NTIMES; i ++)

    {

    MPI_Irecv(&obuf[0],1,MPI_INT,MASTER_RANK,TAG1,comm, &requests[0]);

    MPI_Irecv(&obuf[1],1,MPI_INT,MASTER_RANK,TAG1,comm, &requests[1]);

    MPI_Irecv(&obuf[2],1,MPI_INT,MASTER_RANK,TAG1,comm, &requests[2]);

    MPI_Irecv(&obuf[3],1,MPI_INT,MASTER_RANK,TAG1,comm, &requests[3]);

    MPI_Irecv(&obuf[4],1,MPI_INT,MASTER_RANK,TAG1,comm, &requests[4]);

    MPI_Waitall(5,requests,statuses);

    }

    t /= (double) NTIMES;

    t *= MILLION;

    MASTER printf(«%gtt5 async. sendsn»,t);

    fflush(stdout);

    /* ———— Проверка посылкиприема сигнала ———- */

    t = 0;

    if(i_am_the_master)

    for(i = 0; i < NTIMES_LATENCY; i ++)

    {

    t1 = Wtime();

    /* Was:

    MPX_Sig_send(1,TAG1,comm);

    MPX_Sig_wait(1,TAG1,comm);

    */

    MPI_Send(NULL,0,MPI_INT,1,TAG1,comm);

    MPI_Recv(NULL,0,MPI_INT,1,TAG1,comm,&status);

    t2 = Wtime();

    t += (t2 — t1 — dt);

    }

    else if(myid == 1)

    for(i = 0; i < NTIMES_LATENCY; i ++)

    {

    MPI_Recv(NULL,0,MPI_INT,MASTER_RANK,TAG1,comm,&status);

    MPI_Send(NULL,0,MPI_INT,MASTER_RANK,TAG1,comm);

    }

    t /= (double) NTIMES_LATENCY;

    t *= MILLION;

    MASTER printf(«%gttsignal sending (latency)n»,t);

    fflush(stdout);

    }

    Вывод результата работы программы для np=5

    call_batch: calling batch

    MPItest/C 1.0: running 5 processes…

    Testing basic MPI routines.

    ** Timing for MPI operations in microseconds ***

    0.655001 Timing

    60.8215 Barrier

    48.7546 Global sum (Allreduce)

    52.8754 10 global sums (Allreduce)

    48.4172 Global sum (Reduce)

    48.7901 10 Global sums (Reduce)

    26.4905 Broadcast

    8.60672 Gather

    0.908893 Non-blocking send with wait

    0.909001 Blocking vector send

    4.42849 SendRecv

    5.79681 Send and receive

    7712.04 5 sends in one

    Вывод результата работы программы для np=32

    call_batch: calling batch

    MPItest/C 1.0: running 32 processes…

    Testing basic MPI routines.

    ** Timing for MPI operations in microseconds ***

    0.614616 Timing

    172.913 Barrier

    143.482 Global sum (Allreduce)

    152.485 10 global sums (Allreduce)

    138.163 Global sum (Reduce)

    116.008 10 Global sums (Reduce)

    58.7728 Broadcast

    17.9147 Gather

    1.63228 Non-blocking send with wait

    1.2411 Blocking vector send

    4.4676 SendRecv

    5.83727 Send and receive

    A9 Пример программы с использованием профилировочных библиотек MPE ( cpilog.c)

    Эта программа вычисляет число pi , и выводит информацию о времени вычисления для каждого процесса отдельно. Программа использует профилировочные библиотеки MPE. Просмотр файла регистрационного журнала cpilog.clog можно выполнить при помощи утилиты Jumpshot-2

    #include «mpi.h»

    #include «mpe.h»

    #include

    #include

    double f( double );

    double f( double a)

    {

    return (4.0 / (1.0 + a*a));

    }

    int main( int argc, char *argv[])

    {

    int n, myid, numprocs, i, j;

    double PI25DT = 3.141592653589793238462643;

    double mypi, pi, h, sum, x;

    double startwtime = 0.0, endwtime;

    int namelen;

    int event1a, event1b, event2a, event2b,

    event3a, event3b, event4a, event4b;

    char processor_name[MPI_MAX_PROCESSOR_NAME];

    MPI_Init(&argc,&argv);

    MPI_Comm_size(MPI_COMM_WORLD,&numprocs);

    MPI_Comm_rank(MPI_COMM_WORLD,&myid);

    MPI_Get_processor_name(processor_name,&namelen);

    fprintf(stderr,»Process %d running on %sn», myid, processor_name);

    /*

    MPE_Init_log() & MPE_Finish_log() не нуждается в библиотеке liblmpe.a. Но в случае ее прилинкования ,

    MPI_Init() автоматически вызовет процедуре MPE_Init_log().

    */

    /*

    MPE_Init_log();

    */

    /* Назначение ID номера от MPE, пользователю не надо делать это вручную */

    event1a = MPE_Log_get_event_number();

    event1b = MPE_Log_get_event_number();

    event2a = MPE_Log_get_event_number();

    event2b = MPE_Log_get_event_number();

    event3a = MPE_Log_get_event_number();

    event3b = MPE_Log_get_event_number();

    event4a = MPE_Log_get_event_number();

    event4b = MPE_Log_get_event_number();

    if (myid == 0) {

    MPE_Describe_state(event1a, event1b, «Broadcast», «red»);

    MPE_Describe_state(event2a, event2b, «Compute», «blue»);

    MPE_Describe_state(event3a, event3b, «Reduce», «green»);

    MPE_Describe_state(event4a, event4b, «Sync», «orange»);

    }

    if (myid == 0)

    {

    n = 1000000;

    startwtime = MPI_Wtime();

    }

    MPI_Barrier(MPI_COMM_WORLD);

    MPE_Start_log();

    for (j = 0; j < 5; j++)

    {

    MPE_Log_event(event1a, 0, «start broadcast»);

    MPI_Bcast(&n, 1, MPI_INT, 0, MPI_COMM_WORLD);

    MPE_Log_event(event1b, 0, «end broadcast»);

    MPE_Log_event(event4a,0,»Start Sync»);

    MPI_Barrier(MPI_COMM_WORLD);

    MPE_Log_event(event4b,0,»End Sync»);

    MPE_Log_event(event2a, 0, «start compute»);

    h = 1.0 / (double) n;

    sum = 0.0;

    for (i = myid + 1; i <= n; i += numprocs)

    {

    x = h * ((double)i — 0.5);

    sum += f(x);

    }

    mypi = h * sum;

    MPE_Log_event(event2b, 0, «end compute»);

    MPE_Log_event(event3a, 0, «start reduce»);

    MPI_Reduce(&mypi, &pi, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);

    MPE_Log_event(event3b, 0, «end reduce»);

    }

    /*

    MPE_Finish_log(«cpilog»);

    */

    if (myid == 0)

    {

    endwtime = MPI_Wtime();

    printf(«pi is approximately %.16f, Error is %.16fn»,

    pi, fabs(pi — PI25DT));

    printf(«wall clock time = %fn», endwtime-startwtime);

    }

    MPI_Finalize();

    return(0);

    }

    Для просмотра файла регистрационного журнала в текстовом режиме можно использовать утилиту clog_print, например:

    ./clog_print cpilog.clog >> readfile

    Для данного примера файл регистрационного журнала будет следующим:

    ts=0.000000 type=loc len=9, pid=0 srcid=900 line=339 file=clog.c

    ts=0.000000 type=comm len=5, pid=0 et=init pt=-1 ncomm=91 srcid=900

    ts=0.000000 type=loc len=9, pid=0 srcid=901 line=284 file=clog.c

    ts=0.000000 type=raw len=7, pid=0 id=-201 data=-1 srcid=901 desc=MPI_PROC_NULL

    ts=0.000000 type=raw len=7, pid=0 id=-201 data=-2 srcid=901 desc=MPI_ANY_SOURCE

    ts=0.000000 type=raw len=7, pid=0 id=-201 data=-1 srcid=901 desc=MPI_ANY_TAG

    ts=0.001953 type=loc len=9, pid=0 srcid=902 line=364 file=clog.c

    ts=0.001953 type=sdef len=10, pid=0 id=200 start=500 end=501 color=red desc=Broa

    ts=0.001953 type=sdef len=10, pid=0 id=201 start=502 end=503 color=blue desc=Com

    ts=0.001953 type=sdef len=10, pid=0 id=202 start=504 end=505 color=green desc=Re

    ts=0.001953 type=sdef len=10, pid=0 id=203 start=506 end=507 color=orange desc=S

    ts=0.001953 type=raw len=7, pid=0 id=11 data=1 srcid=901 desc=

    ts=0.001953 type=raw len=7, pid=0 id=12 data=2 srcid=901 desc=

    ts=0.001953 type=raw len=7, pid=0 id=500 data=0 srcid=901 desc=start broadcast

    ts=0.001953 type=raw len=7, pid=0 id=13 data=1 srcid=901 desc=

    ts=0.001953 type=raw len=7, pid=0 id=14 data=2 srcid=901 desc=

    ts=0.001953 type=raw len=7, pid=0 id=501 data=0 srcid=901 desc=end broadcast

    ts=0.001953 type=raw len=7, pid=0 id=506 data=0 srcid=901 desc=Start Sync

    ts=0.001953 type=raw len=7, pid=0 id=11 data=3 srcid=901 desc=

    ts=0.001953 type=raw len=7, pid=0 id=12 data=4 srcid=901 desc=

    ts=0.001953 type=raw len=7, pid=0 id=507 data=0 srcid=901 desc=End Sync

    ts=0.001953 type=raw len=7, pid=0 id=502 data=0 srcid=901 desc=start compute

    ts=0.020508 type=raw len=7, pid=0 id=503 data=0 srcid=901 desc=end compute

    ts=0.020508 type=raw len=7, pid=0 id=503 data=0 srcid=901 desc=end compute

    ts=0.020508 type=raw len=7, pid=0 id=504 data=0 srcid=901 desc=start reduce

    ts=0.020508 type=raw len=7, pid=0 id=25 data=1 srcid=901 desc=

    ts=0.020508 type=raw len=7, pid=0 id=26 data=2 srcid=901 desc=

    The behavior of MPI functions upon error has slightly changed with the latest standards. It used to be managed with the MPI_Errhandler_{get|set|create}() functions (deprecated since MPI 2.0 and removed since MPI 3.0).
    It is now managed through the MPI_{Comm|Win|File}_{get|set|create}_errhandler() functions. This gives much greater level of possible adjustments in this management.

    There are two predefined error handlers that all MPI libraries propose (although some more can be proposed as well):

    • MPI_ERRORS_ARE_FATAL which aborts the entire program whenever an error occurs within an associated MPI call; and
    • MPI_ERRORS_RETURN which simply returns from the associated MPI call upon error, with the corresponding error code.

    By default, the behavior is that all MPI calls but the ones associated with Input/Output actions trigger abortion in case of error. Conversely, the MPI-IO calls will normally return from error with the corresponding error code. Actually, the standard is a bit less prescriptive and says:

    By default, communication errors are fatal — MPI_ERRORS_ARE_FATAL
    is the default error handler associated with MPI_COMM_WORLD. I/O
    errors are usually less catastrophic (e.g., «file not found») than
    communication errors, and common practice is to catch these errors and
    continue executing.

    So to answer plainly to your questions, if you want to prevent the code from crashing upon error, catch them and implement some contingency procedure, you have mostly two solutions:

    1. Ad-hoc solution: set the error handler to be MPI_ERRORS_RETURN for the communicator, file or window you want and check the error code upon completion of the associated MPI calls. You will then have to take action based on the exact error returned each time, bearing in mind that once an error occurred inside a MPI call, there is no guanranty that any further MPI call will succeed. Indeed, there are all chances that any subsequent call to MPI will crash.
    2. More elaborated: create a custom error handler which will possibly print extra details that you might want to see or take further useful actions, before to either return or abort. You can create several different of these and associate them selectively to the communicators, windows or files you want. You can even think, if you are coding in C++, of creating your own exception classes and raising them this way.

    But again, the fact that no MPI call is guaranteed to succeed after a first error was encountered within the library greatly limits the scope of what can be done, so most of the time, the default behavior is perfectly suited and can be kept untouched.

    The behavior of MPI functions upon error has slightly changed with the latest standards. It used to be managed with the MPI_Errhandler_{get|set|create}() functions (deprecated since MPI 2.0 and removed since MPI 3.0).
    It is now managed through the MPI_{Comm|Win|File}_{get|set|create}_errhandler() functions. This gives much greater level of possible adjustments in this management.

    There are two predefined error handlers that all MPI libraries propose (although some more can be proposed as well):

    • MPI_ERRORS_ARE_FATAL which aborts the entire program whenever an error occurs within an associated MPI call; and
    • MPI_ERRORS_RETURN which simply returns from the associated MPI call upon error, with the corresponding error code.

    By default, the behavior is that all MPI calls but the ones associated with Input/Output actions trigger abortion in case of error. Conversely, the MPI-IO calls will normally return from error with the corresponding error code. Actually, the standard is a bit less prescriptive and says:

    By default, communication errors are fatal — MPI_ERRORS_ARE_FATAL
    is the default error handler associated with MPI_COMM_WORLD. I/O
    errors are usually less catastrophic (e.g., «file not found») than
    communication errors, and common practice is to catch these errors and
    continue executing.

    So to answer plainly to your questions, if you want to prevent the code from crashing upon error, catch them and implement some contingency procedure, you have mostly two solutions:

    1. Ad-hoc solution: set the error handler to be MPI_ERRORS_RETURN for the communicator, file or window you want and check the error code upon completion of the associated MPI calls. You will then have to take action based on the exact error returned each time, bearing in mind that once an error occurred inside a MPI call, there is no guanranty that any further MPI call will succeed. Indeed, there are all chances that any subsequent call to MPI will crash.
    2. More elaborated: create a custom error handler which will possibly print extra details that you might want to see or take further useful actions, before to either return or abort. You can create several different of these and associate them selectively to the communicators, windows or files you want. You can even think, if you are coding in C++, of creating your own exception classes and raising them this way.

    But again, the fact that no MPI call is guaranteed to succeed after a first error was encountered within the library greatly limits the scope of what can be done, so most of the time, the default behavior is perfectly suited and can be kept untouched.

    Понравилась статья? Поделить с друзьями:
  • Ошибка на уаз патриот 0171
  • Ошибка на торговой площадке стим
  • Ошибка на титульном листе трудовой книжки как исправить
  • Ошибка на терминале alert irruption
  • Ошибка на терминале 4134 сбербанк как исправить