序列化嵌套结构以在 MPI 中发送/接收

问题描述 投票:0回答:1

基本上我正在创建一个并行程序来计算 +50000 x 50000 像素的 julia 集图像,我正在使用 MPI 和 PNG lib 来这样做。我有一个结构

typedef struct Block
{
    int size;
    int place;
    png_bytep *rows;
} block;

block knapsack;
png_bytep *row_pointers;

的分配函数
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]); 打印出内容后,在系统周围发送嵌套结构时遇到了很多麻烦;我可以看到它们在发送后不匹配。我一直在调查 I 并意识到因为我的指针不在连续块中所以发送不正确。我一直在研究序列化我的背包并遇到平面缓冲区和协议缓冲区。这些看起来太复杂了,很难找到一个好的教程。我的另一个选择似乎是 MPI_Pack() 但我不确定时间增加会有多糟糕,因为整个目标是尽可能高地推动它并快速完成。有没有人对解决这个问题的最佳方法有任何建议?谢谢!

c serialization mpi flatbuffers
1个回答
0
投票

几个问题...

你没有真正的二维数组。您有一个指向行的一维指针数组,这些行是一维像素数组(例如,

png_bytep
是指向
png_byte
像素数组的指针)。

由于额外的指针间接寻址,这可能会很慢。除非你的函数严格在行上运行。

可能拥有真正的二维像素阵列(例如)更好:

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)
{
    img_data = malloc(sizeof(png_byte) * height * width);

    knapsack.height = height;
    knapsack.width = width;
    knapsack.size = height * width;

    knapsack.data = img_data;
}

你的

MPI_Send
[和
MPI_Recv
]只需发送
struct
.

但是,在[非零]等级接收到它之后,

png_byte *data;
指针是无意义因为它是等级0进程的地址空间内的地址。

发送结构告诉接收者geometry(即高度/宽度)和其他元数据,但它发送实际数据。

虽然 [可能] 有一些更高级的

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);

接收器:

// send 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(sizeof(png_byte) * knapsack.size);

// receive the data matrix
MPI_Receive(knapsack.data, knapsack.size, MPI_BYTE, 1, 1, MPI_COMM_WORLD);

以上假设您要将entire数组发送到每个[worker]等级。这是最简单的。

但是,在你开始工作之后,你可能想使用

MPI_Scatter/MPI_Gather
代替将部分/子矩阵发送到每个工人等级。也就是说,每个等级仅在全矩阵的二维子窗口上运行。

当然,您可以仍然使用row指针,但是传输

knapsack.data
的实际调用需要是一个循环,每个循环都有一个单独的调用
row


有关如何拆分数据以提高性能的一些其他信息,请参阅我最近的 [!] 回答:在 MPI 中发送自定义结构

旁注: 如果您的节点将位于同一物理处理器系统上(例如,您有一台 128 核机器)并且所有内核都可以映射/共享相同的内存,那么您最好使用

pthreads
openmp
.当然,开销会更少,而且程序可能对缓存更友好。 YMMV ...

© www.soinside.com 2019 - 2024. All rights reserved.