使用接近`INT_MAX`的`count`值来传递数据

消息传递接口API始终使用int作为count变量的类型。 例如, MPI_Send的原型是:

 int MPI_Send(const void* buf, int count, MPI_Datatype datatype, int dest, int tag, MPI_Comm comm); 

如果要发送或接收的元素数量增长接近或甚至超过 INT_MAX则这可能是一个问题。

当然,这个问题可以解决,通过以下方式降低count

  1. 将单个呼叫分成多个呼叫
  2. 定义(不必要的)聚合MPI_Datatype

无论如何,这两种方法都不是真正的解决方案,特别是如果使用简单的启发式方法实现的话。 因此,我想问的是:

使用标准MPI呼叫处理这类案件是否有更好的习惯用法? 如果没有,是否有人知道围绕MPI构建的一些(可靠的)包装器库来克服这个限制?

MPI论坛非常不愿意对MPI API进行重大更改以引入64位支持。 这样做的原因是保持向后兼容性而不引入很少使用的function – 它似乎与使Fortran 2xxx与史前FORTRAN IV程序兼容的程序几乎同样有力。

从票证中可以看出,创建一个大型数据类型来解决这个限制实际上被许多人认为不是那么苛刻的解决方案,即使是William D. Gropp本人 :

首先,可以通过简单地创建适当的MPI数据类型来发送更大的数据(这可能更容易,但它是可能的)。 其次,发送如此大的数据将在当前平台上花费几秒钟(至少!)(8GB仅用于4字节整数和2GB计数) – 所以这不应该是常见操作(以及创建和提交和释放数据类型应该可以忽略不计)。

事实上,MPI-3.0引入了构建大型(超过2 31个元素)数据类型的官方支持这一事实,而改变MPI_SENDMPI_Count / INTEGER(KIND=MPI_COUNT_KIND)等调用的count参数的提议被拒绝了,这暗示着你的方式认为这是MPI论坛的主流。 甚至在MPI-3.0之前,多年来一些实现使用了64位内部大小(例如Open MPI),而其他人则选择保留32位流程(例如Intel MPI)。

我是BigMPI的首席开发人员,并与他人共同撰写了一篇题为“INT_MAX ……以及超越!”的论文:探索MPI中的大量支持,在这里比空间许可更详细地讨论了这个确切的主题。

如果您无法自由访问ACM DL,您可以下载Argonne预印本或结帐纸张来源回购 。

以下是此项工作的主要亮点:

  • BigMPI是一个相对高质量的MPI接口,支持64b整数计数(类型在技术上是MPI_CountMPI_Aint在内部使用)。 具有讽刺意味的是,它没有使用MPI-3大数量function。 这是因为BigMPI并不完全通用,而是旨在支持最常见的使用模式。

  • BigMPI的设计部分是为了教育。 它采用超宽容的MIT许可证 ,使任何人都可以将代码从中复制到另一个项目中,可能需要进行更改以满足不可预见的需求。

  • 在MPI-3接口中超过INT_MAX不仅仅是一个问题。 这是无效的ISO C代码。 有符号整数的翻转行为 – 与无符号整数不同 – 未定义。 因此,主要问题不在于MPI,而在于C整数不能容纳大于INT_MAX的数字。 如果MPI存在问题,那么count参数被指定为C int类型,而不是size_t ,这是一个争论的问题。 在说MPI应该切换到size_t ,您需要了解MPI的历史以及ABI兼容性对MPI用户子集的重要性。

  • 即使使用BigMPI或类似的基于数据类型的方法, 实现也可能存在错误。 这意味着执行符合标准的操作将无法正常工作,因为内部的MPI实现可能会将count*sizeof(type)等内容不正确地存储到32b值中,如果sizeof(type)为,则可能会溢出有效计数,如10亿八,例如。 正如前面提到的论文中所指出的,除了这些错误 – 在MPICH和Open-MPI的最新版本中似乎不存在 – 还必须减轻POSIX函数中的错误。

  • Fortran的情况更复杂。 未指定Fortran缺省整数大小,理论上MPI实现应尊重编译器使用的任何内容。 然而,实际情况往往并非如此。 我相信由于在内部使用C int许多MPI实现因INT_MAX以上的计数而被破坏。 BigMPI没有Fortran界面,虽然我有一天想写一个。 在那之前,请讨论MPI实施者做正确的事情,将Fortran INTEGER内部转换为C类型。

无论如何,我不希望将我们论文的全部内容转录到这篇文章中,特别是因为它是免费提供的,源代码也是如此。 如果您觉得这篇文章不合适,请发表评论,我会稍后再尝试添加。

最后,BigMPI是研究代码,我不会说它已经完成(但是,你不应该点击未完成的代码)。 强烈建议用户在用于生产之前对BigMPI和MPI实现执行自己的正确性测试。

我不知道任何处理这个的现有包装器,但你可以编写自己的包装器。 大多数MPI实现都有一个用于分析(PMPI)的附加层。 您可以将此图层用于其他目的,在这种情况下拆分消息。 该层的工作方式是调用所需的MPI函数,并立即调用该函数的PMPI版本。 您可以编写MPI版本的包装器,它将拆分消息并为每个消息调用PMPI版本。 这是我很久以前写的用于拆分MPI_Bcast的一个非常简单的例子:

 #include  int MPI_Bcast(void* buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm ) { /* This function is a simple attempt at automatically splitting MPI messages, in this case MPI_Bcast. By utilizing the profiling interface of MPI, this function is able to intercept a call to MPI_Bcast. Then, instead of the typical profiling, the message size is checked. If the message is larger than the maximum allowable size, it will be split into multiple messages, each of which will be sent individually. This function isnot intended for high performance, it is intended to add capability without requiring access to the source code of either the MPI implementation or the program using MPI. The intent is to compile this as a shared library and preload this library to catch MPI calls. */ int result; int typesize; long totalsize; long maxsize=1; // Set the maximum size of a single message maxsize=(maxsize<<31)-1; // Get the size of the message to be sent MPI_Type_size(datatype, &typesize); totalsize=static_cast(typesize)*static_cast(count); // Check the size if (totalsize > maxsize) { // The message is too large, split it /* Ideally, this should be tailored to the system, possibly split into a minimum of equally sized messages that will fit into the maximum message size. However, this is a very simple implementation, and is focusing on proof of concept, not efficiency. */ int elementsPerChunk=maxsize/typesize; // Number of elements per chunk int remCount=count; // Remaining number of elements char *address=static_cast(buffer); // Starting address // Cast to char to perform arithmetic int nChunks=count/elementsPerChunk; // How many chunks to send if (count%elementsPerChunk!=0) nChunks++; // One more for any remaining elements int chunkCount; // Number of elements in current chunk // Send one chunk at a time for (int i=0;ielementsPerChunk) { chunkCount=elementsPerChunk; } else { chunkCount=remCount; } // Decrement the remaining elements remCount-=chunkCount; // Send the message chunk /* There is room for improvement here as well. One key concern is the return value. Normally, there would be a single return value for the entire operation. However, as the operation is split into multiple operations, each with its own return value, a decision must be made as to what to return. I have chosen to simply use the return value from the last call. This skips over some error checking but is not critical at present. */ result=PMPI_Bcast(static_cast(address),chunkCount,datatype,root,comm); // Update the address for the next chunk address+=chunkCount*typesize; } } else { // The message is small enough, just send as it is result=PMPI_Bcast(buffer,count,datatype,root,comm); } // Pass the return value back to the caller return result; } 

您可以为MPI_Send(和MPI_Recv)编写类似的东西,并获得所需的function。 但是,如果这仅适用于一个程序,那么最好只修改该程序以便以块的forms发送。

我自己没有使用它,但是有一个包装器可以帮助你调用BigMPI 。 你将不得不看看Github自述文件以了解更多关于如何使用它的信息,但我认为它会解决这个问题的一些弊端。