Если вы ориентируетесь только на платформы, поддерживающие интерфейс профилирования PMPI, то существует общее решение, требующее минимальных изменений исходного кода. Основная идея состоит в том, чтобы (ab-) использовать интерфейс PMPI для обертки. Вероятно, в некотором не-OO понимается реализация шаблона моста.
Во-первых, несколько наблюдений. Существует один тип структуры, определенный в стандарте MPI, и это MPI_Status
. Он имеет только три общедоступных поля: MPI_SOURCE
, MPI_TAG
и MPI_ERR
. Функция MPI не принимает значение MPI_Status
. В стандарте определяются следующие непрозрачные типы: MPI_Aint
, MPI_Count
, MPI_Offset
и MPI_Status
(для обеспечения ясности здесь снижается несколько типов совместимости Fortran). Первые три являются интегральными. Тогда существует 10 типов ручек, от MPI_Comm
до MPI_Win
. Ручки могут быть реализованы либо как специальные целочисленные значения, либо как указатели на внутренние структуры данных. MPICH и другие реализации, основанные на нем, используют первый подход, в то время как Open MPI занимает второй. Являясь указателем или целым числом, дескриптор любого типа может вписываться в один тип данных C, а именно: intptr_t
.
Основная идея заключается в том, чтобы переопределить все функции MPI и пересмотреть свои аргументы, чтобы быть из intptr_t
типа, то есть пользователь скомпилированного кода сделать переход к соответствующему типу и сделать фактический MPI вызов:
В mytypes.h
:
typedef intptr_t my_MPI_Datatype;
typedef intptr_t my_MPI_Comm;
В mympi.h
:
#include "mytypes.h"
// Redefine all MPI handle types
#define MPI_Datatype my_MPI_Datatype
#define MPI_Comm my_MPI_Comm
// Those hold the actual values of some MPI constants
extern MPI_Comm my_MPI_COMM_WORLD;
extern MPI_Datatype my_MPI_INT;
// Redefine the MPI constants to use our symbols
#define MPI_COMM_WORLD my_MPI_COMM_WORLD
#define MPI_INT my_MPI_INT
// Redeclare the MPI interface
extern int MPI_Send(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm);
В mpiwrap.c
:
#include <mpi.h>
#include "mytypes.h"
my_MPI_Comm my_MPI_COMM_WORLD;
my_MPI_Datatype my_MPI_INT;
int MPI_Init(int *argc, char ***argv)
{
// Initialise the actual MPI implementation
int res = PMPI_Init(argc, argv);
my_MPI_COMM_WORLD = (intptr_t)MPI_COMM_WORLD;
my_MPI_INT = (intptr_t)MPI_INT;
return res;
}
int MPI_Send(void *buf, int count, intptr_t datatype, int dest, int tag, intptr_t comm)
{
return PMPI_Send(buf, count, (MPI_Datatype)datatype, dest, tag, (MPI_Comm)comm);
}
В коде:
#include "mympi.h" // instead of mpi.h
...
MPI_Init(NULL, NULL);
...
MPI_Send(buf, 10, MPI_INT, 1, 10, MPI_COMM_WORLD);
...
ИМБ обертка может быть либо связаны статически или динамически предварительно. Оба способа работают до тех пор, пока реализация MPI использует слабые символы для интерфейса PMPI.Вы можете расширить приведенный выше пример кода, чтобы охватить все функции и константы MPI. Все константы должны быть сохранены в обертке MPI_Init
/MPI_Init_thread
.
Обработка MPI_Status
является чем-то запутанным. Хотя стандарт определяет публичные поля, он ничего не говорит о своем заказе или размещении в структуре. И еще раз, MPICH и Open MPI существенно различаются:
// MPICH (Intel MPI)
typedef struct MPI_Status {
int count_lo;
int count_hi_and_cancelled;
int MPI_SOURCE;
int MPI_TAG;
int MPI_ERROR;
} MPI_Status;
// Open MPI
struct ompi_status_public_t {
/* These fields are publicly defined in the MPI specification.
User applications may freely read from these fields. */
int MPI_SOURCE;
int MPI_TAG;
int MPI_ERROR;
/* The following two fields are internal to the Open MPI
implementation and should not be accessed by MPI applications.
They are subject to change at any time. These are not the
droids you're looking for. */
int _cancelled;
size_t _ucount;
};
Если вы используете только MPI_Status
, чтобы получить информацию из вызовов, таких как MPI_Recv
, то это тривиально, чтобы скопировать три открытых поля в заданной пользователем статической структуры содержащие только те поля. Но этого будет недостаточно, если вы также используете функции MPI, которые читают непубличные, например. MPI_Get_count
. В этом случае, тупой, не OO подход просто встроить оригинальную структуру статус:
В mytypes.h
:
// 64 bytes should cover most MPI implementations
#define MY_MAX_STATUS_SIZE 64
typedef struct my_MPI_Status
{
int MPI_SOURCE;
int MPI_TAG;
int MPI_ERROR;
char _original[MY_MAX_STATUS_SIZE];
} my_MPI_Status;
В mympi.h
:
#define MPI_Status my_MPI_Status
#define MPI_STATUS_IGNORE ((my_MPI_Status*)NULL)
extern int MPI_Recv(void *buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm, MPI_Status *status);
extern int MPI_Get_count(MPI_Status *status, MPI_Datatype datatype, int *count);
В mpiwrap.c
:
int MPI_Recv(void *buf, int count, my_MPI_Datatype datatype, int dest, int tag, my_MPI_Comm comm, my_MPI_Status *status)
{
MPI_Status *real_status = (status != NULL) ? (MPI_Status*)&status->_original : MPI_STATUS_IGNORE;
int res = PMPI_Recv(buf, count, (MPI_Datatype)datatype, dest, tag, (MPI_Comm)comm, real_status);
if (status != NULL)
{
status->MPI_SOURCE = real_status->MPI_SOURCE;
status->MPI_TAG = real_status->MPI_TAG;
status->MPI_ERROR = real_status->MPI_ERROR;
}
return res;
}
int MPI_Get_count(my_MPI_Status *status, my_MPI_Datatype datatype, int *count)
{
MPI_Status *real_status = (status != NULL) ? (MPI_Status*)&status->_original : MPI_STATUS_IGNORE;
return PMPI_Get_count(real_status, (MPI_Datatype)datatype, count);
}
В вашем коде:
#include "mympi.h"
...
MPI_Status status;
int count;
MPI_Recv(buf, 100, MPI_INT, 0, 10, MPI_COMM_WORLD, &status);
MPI_Get_count(&status, MPI_INT, &count);
...
Ваша система сборки должна затем проверить, если sizeof(MPI_Status)
фактической реализации MPI меньше или равна MY_MAX_STATUS_SIZE
.
Вышеупомянутая информация является просто быстрой и грязной идеей - не проверял ее, а некоторые const
или отливки могут отсутствовать здесь или там. Он должен работать на практике и быть достаточно удобным.
Как насчет того, чтобы проиллюстрировать попытку в коде, а затем определить конкретную проблему, из-за которой попытка не работать? В противном случае это кажется очевидным применением шаблона Bridge. – jxh
'// заголовок (предоставленный нами) int my_stub_mpi_send (const void * buf, int count, void * datatype, int dest, int tag, void * comm); //*.c (при условии, пользователем) #include #include INT my_stub_mpi_send (сопзИте пустоту * ЬаЯ, количество INT, недействительный * тип данных, Int Dest, Int тег, недействительный * Прдч) { return MPI_Send (* buf, count, * ((MPI_Datatype *) тип данных), dest, tag, * ((MPI_Comm *) comm)); } // Примечания: (1) Скорее всего, интерфейс будет C, а не C++, если только я не смогу сделать убедительный пример для C++; // (2) Цель здесь - избегать указателей void, если это возможно; –
Основная проблема здесь в том, что типы MPI варьируются от одной реализации к другой. В подходе C++ я бы использовал декларации функций шаблона шаблона. Но даже это проблема, потому что тогда реализация (* .cpp-файл) должна была бы явно создавать экземпляры, так как их определения не присутствуют в файле заголовка. Я предпочел бы держаться подальше от явных шаблонных экземпляров, потому что они могут взорваться экспоненциально. Было бы неплохо использовать шаблоны проектирования Bridge или Adapter, но это предполагает общие абстрактные основы среди всех реализаций MPI для каждого типа MPI, чего, вероятно, слишком много, чтобы предположить. –