基本上我正在创建一个并行程序来计算 +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() 但我不确定时间增加会有多糟糕,因为整个目标是尽可能高地推动它并快速完成。有没有人对解决这个问题的最佳方法有任何建议?谢谢!
几个问题...
你没有真正的二维数组。您有一个指向行的一维指针数组,这些行是一维像素数组(例如,
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 ...