这些是我在为使用MPI(在C ++中)并行运行的算法设计错误处理时遇到的一些常见问题:
- 异常是否在并行执行的代码中工作?是否定义了行为?
- 他们是如何工作的?对于不同的实现,这有何不同?
- 这是好的做法 - 还是应该使用返回代码?
这些是我在为使用MPI(在C ++中)并行运行的算法设计错误处理时遇到的一些常见问题:
MPI代码中的异常与串行代码的作用相同,但如果可能没有在通信器中的所有进程上引发异常,或者您很容易导致死锁,则必须非常小心。
MPI_Barrier(comm); /* Or any synchronous call */
if (!rank) throw Exception("early exit on rank=0");
MPI_Barrier(comm); /* rank>0 deadlocks here because rank=0 exited early */
所有错误处理方法都存在这个问题,很难从通信器上不一致的错误中恢复。在上面的例子中,你可以执行一个 MPI_Allreduce
所以所有级别选择相同的分支。
我的首选是调用错误处理程序并将它们传播到堆栈中,因为这会给我提供最有用/最详细的错误消息,并且很容易捕获断点(或者错误处理程序可以将调试器附加到自身并将其发送给您xterm中的工作站)。
在理想的世界中,您可以使用它们来做您所要求的事情。 “理想世界”是指您可以选择MPI实现并且能够自己管理它(而不是说服集群所有者为您重新配置它)。异常的最小配置包括: --with-例外 国旗,可能还有一些。
我最常使用LAM,默认情况下禁用例外。我相信这也是其他实现的默认设置。
它们的工作方式与'vanilla'C ++异常相同。他们确实在并行执行的代码中工作。
在启动代码中的某个时刻,您希望启用它们:
MPI::COMM_WORLD.Set_errhandler ( MPI::ERRORS_THROW_EXCEPTIONS );
(如果您的库未配置为允许例外,这可能是个坏主意 - 根据LAM,行为“未定义”)
接着:
try { /* something that can fail */ }
catch ( MPI::Exception e ) {
cout << "Oops: " << e.Get_error_string() << e.Get_error_code();
MPI::COMM_WORLD.Abort (-1) ;
}
至于它的好坏,我实在无法说。我没有看到在强化的MPI黑客编写的代码中广泛使用它们,但这可能是因为根据我的经验,代码通常比C ++更多。
错误代码和异常之间的中间地带可能是错误处理程序,简而言之,您可以分配在发生特定错误(由代码指定)时将调用的函数。如果您无法让管理员加入启用例外,那么这可能是一个选项。
并行执行期间异常是否有效取决于您的编译器和MPI库实现。如果你想要可移植的行为,我会避免在该上下文中抛出异常。
如果您想要更多有关错误的详细信息而不仅仅是数字返回码,您当然可以返回和/或传递错误字符串或其他对象(当然,在同一进程内或通过MPI)。