提问人:Max Rush 提问时间:3/26/2023 更新时间:3/26/2023 访问量:111
序列化嵌套结构以在 MPI 中发送/接收
Serializing a Nested Struct to send/recv in MPI
问:
基本上,我正在创建一个并行程序来计算 +50000 x 50000 像素的 julia 集图像,并且我正在使用 MPI 和 PNG lib 来执行此操作。我有一个结构
typedef struct Block
{
int size;
int place;
png_bytep *rows;
} block;
block knapsack;
png_bytep *row_pointers;
和 allocate 函数
void allocate()
{
row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * height);
for (y = 0; y < height; y++)
row_pointers[y] = (png_byte *)malloc(sizeof(png_bytep) * width);
}
我有这个功能来创建一个“背包”,这是一个可以分发到其他进程的row_pointers块。(我稍后在解决此消息传递问题时将合并这些函数。
void pack(int index, int size)
{
knapsack.rows = (png_bytep *)malloc(sizeof(png_bytep) * size);
knapsack.size = size;
knapsack.place = index;
for (y = 0; y < size; y++)
{
knapsack.rows[y] = (png_byte *)malloc(sizeof(png_bytep) * width);
knapsack.rows[y] = row_pointers[index + y];
}
}
然后我想做一些类似的事情
MPI_Send(&knapsack, sizeof(knapsack), MPI_BYTE, 1, 1, MPI_COMM_WORLD);
MPI_Recv(&knapsack, sizeof(knapsack), MPI_BYTE, 0, 1, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
将这些指针的块发送到一堆节点进行计算。它最终将在 128 个内核上进行测试。 问题是我在系统周围发送嵌套结构时遇到了很多麻烦,并且在使用 printf(“%x”, knapsack.rows[0]) 打印出内容后;我可以看到它们在发送后不匹配。我一直在研究我并意识到因为我的指针不在连续块中,所以它没有正确发送。我一直在考虑序列化我的背包,并遇到了 flatbuffers 和 protocol buffers。这些似乎过于复杂,很难找到一个好的教程。我的另一个选择似乎是 MPI_Pack(),但我不确定时间增加会有多糟糕,因为整个目标是尽可能高地推动它并快速完成。有没有人对解决这个问题的最佳方法有任何建议?谢谢!
答:
几个问题......
您没有真正的 2D 数组。您有一个指向行的一维指针数组,这些行是像素的一维数组(例如 是指向像素数组的指针)。png_bytep
png_byte
由于额外的指针间接,这可能会很慢。除非您的函数严格地对行进行操作。
拥有真正的 2D 像素数组可能更好(例如):
typedef struct Block {
int size;
int place;
int width;
int height;
png_byte *data;
} block;
block knapsack;
png_byte *img_data;
void
allocate(int height,int width)
{
knapsack.height = height;
knapsack.width = width;
knapsack.size = sizeof(png_byte) * height * width;
knapsack.data = malloc(knapsack.size);
img_data = knapsack.data;
}
您的 [和 ] 只需发送 .MPI_Send
MPI_Recv
struct
但是,在 [非零] 秩收到指针后,指针毫无意义,因为它是秩 0 进程的地址空间内的地址。png_byte *data;
发送结构告诉接收者几何形状(即高度/宽度)和其他元数据,但它不会发送实际数据。
尽管[可能]有一些更高级的调用,但这里有一些使用简单...MPI_*
MPI_Send/MPI_Recv
寄件人:
// send geometry and metadata (the .data pointer will be useless to receiver,
// but that's okay)
MPI_Send(&knapsack, sizeof(knapsack), MPI_BYTE, 1, 1, MPI_COMM_WORLD);
// send the data matrix
MPI_Send(knapsack.data, knapsack.size, MPI_BYTE, 1, 1, MPI_COMM_WORLD);
接收器:
// receive geometry and metadata (the .data pointer will be useless to us,
// but that's okay)
MPI_Recv(&knapsack, sizeof(knapsack), MPI_BYTE, 1, 1, MPI_COMM_WORLD);
// allocate space to receive the data
knapsack.data = malloc(knapsack.size);
// receive the data matrix
MPI_Receive(knapsack.data, knapsack.size, MPI_BYTE, 1, 1, MPI_COMM_WORLD);
上面假设您要将整个数组发送到每个 [worker] 等级。这是最简单的。
但是,在你完成这项工作之后,你可能想改用它来向每个工人等级发送部分/子矩阵。也就是说,每个排名仅在完整矩阵的 2D 子窗口上运行。MPI_Scatter/MPI_Gather
当然,您仍然可以使用行指针,但传输的实际调用需要是一个循环,每个循环都有一个单独的调用knapsack.data
row
有关如何拆分数据以提高性能的其他信息,请参阅我最近的[!] answer: 在 MPI 中发送自定义结构
旁注:如果您的节点将位于同一物理处理器系统上(例如,您有一台 128 核计算机),并且所有内核都可以映射/共享相同的内存,则最好使用 或 。当然,开销会更少,并且程序可能对缓存更友好。YMMV ...pthreads
openmp
评论