Allreduce 与用户定义的函数和 MPI_BOTTOM
Allreduce with user defined function and MPI_BOTTOM
考虑下面的程序,它应该对 double
s 进行一些愚蠢的添加:
#include <iostream>
#include <vector>
#include <mpi.h>
void add(void* invec, void* inoutvec, int* len, MPI_Datatype*)
{
double* a = reinterpret_cast <double*> (inoutvec);
double* b = reinterpret_cast <double*> (invec);
for (int i = 0; i != *len; ++i)
{
a[i] += b[i];
}
}
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
std::vector<double> buffer = { 2.0, 3.0 };
MPI_Op operation;
MPI_Op_create(add, 1, &operation);
MPI_Datatype types[1];
MPI_Aint addresses[1];
int lengths[1];
int count = 1;
MPI_Get_address(buffer.data(), &addresses[0]);
lengths[0] = buffer.size();
types[0] = MPI_DOUBLE;
MPI_Datatype type;
MPI_Type_create_struct(count, lengths, addresses, types, &type);
MPI_Type_commit(&type);
MPI_Allreduce(MPI_IN_PLACE, MPI_BOTTOM, 1, type, operation, MPI_COMM_WORLD);
MPI_Type_free(&type);
MPI_Op_free(&operation);
MPI_Finalize();
std::cout << buffer[0] << " " << buffer[1] << "\n";
}
因为这是更大程序的一部分,我想发送的数据是 1) 在堆上和 2) 由不同的类型组成,我想我必须使用用户定义的类型。
现在这里一定有问题,因为当 运行 和 mpirun -n 2 ./a.out
时程序崩溃了。来自 gdb 的回溯是:
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:158
#1 0x00007ffff65de460 in non_overlap_copy_content_same_ddt () from /usr/local/lib/libopen-pal.so.6
#2 0x00007ffff180a69b in ompi_coll_tuned_allreduce_intra_recursivedoubling () from /usr/local/lib/openmpi/mca_coll_tuned.so
#3 0x00007ffff793bb8b in PMPI_Allreduce () from /usr/local/lib/libmpi.so.1
#4 0x00000000004088b6 in main (argc=1, argv=0x7fffffffd708) at mpi_test.cpp:39
第 39 行是 MPI_Allreduce
调用。这可能是一个愚蠢的错误,但在盯着它看了几个小时之后,我仍然没有看到它。有人发现错误吗?谢谢!
编辑: Open MPI 在执行 in-将减少到全部。它似乎存在于所有版本中,包括开发分支。可以通过关注 issue on GitHub.
来跟踪状态
您的 add
运算符是错误的,因为您没有考虑数据类型的下限。一个合适的解决方案应该是这样的:
void add(void* invec, void* inoutvec, int* len, MPI_Datatype* datatype)
{
MPI_Aint lb, extent;
MPI_Type_get_true_extent(*datatype, &lb, &extent);
double* a = reinterpret_cast <double*> (reinterpret_cast <char*>(inoutvec) + lb);
double* b = reinterpret_cast <double*> (reinterpret_cast <char*>(invec) + lb);
for (int i = 0; i != *len; ++i)
{
a[i] += b[i];
}
}
这将正确访问数据,但仍然是错误的。 *len
将是 1,因为这是您传递给 MPI_Allreduce
的内容,但每个元素后面有两个双打。正确编写的运算符将使用类型自省机制来获取双精度块的长度并将 *len
乘以它,或者简单地将向量长度硬编码为二:
for (int i = 0; i < 2*(*len); i++)
{
a[i] += b[i];
}
考虑下面的程序,它应该对 double
s 进行一些愚蠢的添加:
#include <iostream>
#include <vector>
#include <mpi.h>
void add(void* invec, void* inoutvec, int* len, MPI_Datatype*)
{
double* a = reinterpret_cast <double*> (inoutvec);
double* b = reinterpret_cast <double*> (invec);
for (int i = 0; i != *len; ++i)
{
a[i] += b[i];
}
}
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
std::vector<double> buffer = { 2.0, 3.0 };
MPI_Op operation;
MPI_Op_create(add, 1, &operation);
MPI_Datatype types[1];
MPI_Aint addresses[1];
int lengths[1];
int count = 1;
MPI_Get_address(buffer.data(), &addresses[0]);
lengths[0] = buffer.size();
types[0] = MPI_DOUBLE;
MPI_Datatype type;
MPI_Type_create_struct(count, lengths, addresses, types, &type);
MPI_Type_commit(&type);
MPI_Allreduce(MPI_IN_PLACE, MPI_BOTTOM, 1, type, operation, MPI_COMM_WORLD);
MPI_Type_free(&type);
MPI_Op_free(&operation);
MPI_Finalize();
std::cout << buffer[0] << " " << buffer[1] << "\n";
}
因为这是更大程序的一部分,我想发送的数据是 1) 在堆上和 2) 由不同的类型组成,我想我必须使用用户定义的类型。
现在这里一定有问题,因为当 运行 和 mpirun -n 2 ./a.out
时程序崩溃了。来自 gdb 的回溯是:
#0 __memcpy_sse2_unaligned () at ../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:158
#1 0x00007ffff65de460 in non_overlap_copy_content_same_ddt () from /usr/local/lib/libopen-pal.so.6
#2 0x00007ffff180a69b in ompi_coll_tuned_allreduce_intra_recursivedoubling () from /usr/local/lib/openmpi/mca_coll_tuned.so
#3 0x00007ffff793bb8b in PMPI_Allreduce () from /usr/local/lib/libmpi.so.1
#4 0x00000000004088b6 in main (argc=1, argv=0x7fffffffd708) at mpi_test.cpp:39
第 39 行是 MPI_Allreduce
调用。这可能是一个愚蠢的错误,但在盯着它看了几个小时之后,我仍然没有看到它。有人发现错误吗?谢谢!
编辑: Open MPI 在执行 in-将减少到全部。它似乎存在于所有版本中,包括开发分支。可以通过关注 issue on GitHub.
来跟踪状态您的 add
运算符是错误的,因为您没有考虑数据类型的下限。一个合适的解决方案应该是这样的:
void add(void* invec, void* inoutvec, int* len, MPI_Datatype* datatype)
{
MPI_Aint lb, extent;
MPI_Type_get_true_extent(*datatype, &lb, &extent);
double* a = reinterpret_cast <double*> (reinterpret_cast <char*>(inoutvec) + lb);
double* b = reinterpret_cast <double*> (reinterpret_cast <char*>(invec) + lb);
for (int i = 0; i != *len; ++i)
{
a[i] += b[i];
}
}
这将正确访问数据,但仍然是错误的。 *len
将是 1,因为这是您传递给 MPI_Allreduce
的内容,但每个元素后面有两个双打。正确编写的运算符将使用类型自省机制来获取双精度块的长度并将 *len
乘以它,或者简单地将向量长度硬编码为二:
for (int i = 0; i < 2*(*len); i++)
{
a[i] += b[i];
}