显示 MPI 类型图
Display a MPI typemap
type map
是 MPI 中一个重要但令人困惑的概念。我想要一个例程来为我显示或打印类型图。
例如(取自 MPI-3 标准),
MPI_TYPE_CREATE_RESIZED(MPI_INT, -3, 9, type1)
类型映射中的结果
{(lb_marker, -3), (int, 0), (ub_marker, 6)}.
再次使用该类型:
MPI_TYPE_CONTIGUOUS(2, type1, type2)
类型映射是
{(lb_marker, -3), (int, 0), (int,9), (ub_marker, 15)}
我想要一种自动显示该类型图的方法。
当然可以使用 MPI_Type_get_contents
和 MPI_Type_get_envelope
并递归下降直到找到内置类型。这是一个巨大的痛苦,我本以为会有 20 年的时间来使用某种工具来为我做这件事。
一些很有前途但效果不佳的工具:
我从 ~ 2001 here 找到了 MPImap。首先,它需要针对现代 Tcl/TK 进行更新,修补以解决一些内存错误,然后再进行更新;您会得到一个无响应的 GUI。相反,我正在寻找可以在 运行 时间调用的 library/routine。
MPIDU_Datatype_deubg
是 MPICH 特定的内部类型转储例程。它不显示类型映射(它显示数据循环表示,再次关闭)
曾经有一个名为 XMPI 的调试器,在其功能中列出了显示 MPI 类型映射的能力。这个调试器似乎是特定于 LAM-MPI 的,并且没有使用 get_contents/ get_envelope.
As Rob Latham said, there isn't good pre-existing solutions. With the help of the links given by tim I created this function available on Github. I took your example for the contiguous + resize test (here) 输出为
contiguous + resize
"(LB, -3), (MPI_INT, 0), (MPI_INT, 9), (UB, 15)"
有了这个功能你只需要做printMapDatatype(mydatatype)
。我希望这就是您要搜索的内容。
这是函数,以防万一:
MPI_Aint printdatatype( MPI_Datatype datatype, MPI_Aint prevExtentTot ) {
int *array_of_ints;
MPI_Aint *array_of_adds;
MPI_Datatype *array_of_dtypes;
int num_ints, num_adds, num_dtypes, combiner;
int i, j;
MPI_Type_get_envelope( datatype, &num_ints, &num_adds, &num_dtypes, &combiner );
array_of_ints = (int *) malloc( num_ints * sizeof(int) );
array_of_adds = (MPI_Aint *) malloc( num_adds * sizeof(MPI_Aint) );
array_of_dtypes = (MPI_Datatype *) malloc( num_dtypes * sizeof(MPI_Datatype) );
MPI_Aint extent, subExtent;
MPI_Type_extent(datatype, &extent);
switch (combiner) {
case MPI_COMBINER_NAMED:
// To print the specific type, we can match against the predefined forms.
if (datatype == MPI_BYTE) printf( "(MPI_BYTE, %ld)", prevExtentTot);
else if (datatype == MPI_LB) printf( "(MPI_LB, %ld)", prevExtentTot);
else if (datatype == MPI_PACKED) printf( "(MPI_PACKED, %ld)", prevExtentTot);
else if (datatype == MPI_UB) printf( "(MPI_UB, %ld)", prevExtentTot);
else if (datatype == MPI_CHAR) printf( "(MPI_CHAR, %ld)", prevExtentTot);
else if (datatype == MPI_DOUBLE) printf( "(MPI_DOUBLE, %ld)", prevExtentTot);
else if (datatype == MPI_FLOAT) printf( "(MPI_FLOAT, %ld)", prevExtentTot);
else if (datatype == MPI_INT) printf( "(MPI_INT, %ld)", prevExtentTot );
else if (datatype == MPI_LONG) printf( "(MPI_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_LONG_DOUBLE) printf( "(MPI_LONG_DOUBLE, %ld)", prevExtentTot);
else if (datatype == MPI_LONG_LONG) printf( "(MPI_LONG_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_LONG_LONG_INT) printf( "(MPI_LONG_LONG_INT, %ld)", prevExtentTot);
else if (datatype == MPI_SHORT) printf( "(MPI_SHORT, %ld)", prevExtentTot);
else if (datatype == MPI_SIGNED_CHAR) printf( "(MPI_SIGNED_CHAR, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED) printf( "(MPI_UNSIGNED, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_CHAR) printf( "(MPI_UNSIGNED_CHAR, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_LONG) printf( "(MPI_UNSIGNED_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_LONG_LONG)printf( "(MPI_UNSIGNED_LONG_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_SHORT) printf( "(MPI_UNSIGNED_SHORT, %ld)", prevExtentTot);
else if (datatype == MPI_WCHAR) printf( "(MPI_WCHAR, %ld)", prevExtentTot);
free( array_of_ints );
free( array_of_adds );
free( array_of_dtypes );
return prevExtentTot;
break;
case MPI_COMBINER_DUP:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", \n");
break;
case MPI_COMBINER_CONTIGUOUS:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
for (i=0; i < array_of_ints[0]; i++) {
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
prevExtentTot += subExtent;
printf(", ");
}
break;
case MPI_COMBINER_VECTOR:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("[");
for (i = 0; i < array_of_ints[0]; i++) { //count
printf( "BL : %d - ", array_of_ints[1]);
for (j = 0; j < array_of_ints[2]; j++) { // stride
if (j < array_of_ints[1]) { // if in blocklength
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", ");
}
prevExtentTot += subExtent;
}
}
printf("], ");
break;
case MPI_COMBINER_HVECTOR:
case MPI_COMBINER_HVECTOR_INTEGER:{
MPI_Aint backupPrevExtent = prevExtentTot;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("[");
for (i = 0; i < array_of_ints[0]; i++) { //count
printf( "BL : %d - ", array_of_ints[1]);
for (j = 0; j < array_of_ints[1]; j++) { // blocklength
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", ");
prevExtentTot += subExtent;
}
prevExtentTot = backupPrevExtent + array_of_adds[0]; // + stride un byte
}
printf("], ");
break;
}
case MPI_COMBINER_INDEXED:{
MPI_Aint tmpPrevExtent;
int count, blocklength;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("<");
count = array_of_ints[0];
for (i = 0; i < count; i++) { // count
blocklength = array_of_ints[i + 1]; // array of blocklength
tmpPrevExtent = prevExtentTot;
tmpPrevExtent += array_of_ints[count + 1 + i] * subExtent; // + displacement * size of block
printf( "BL : %d - ", blocklength);
for (j = 0; j < blocklength; j++) { // blocklength
tmpPrevExtent = printdatatype( array_of_dtypes[0], tmpPrevExtent);
printf(", ");
tmpPrevExtent += subExtent;
}
}
printf(">, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_HINDEXED:
case MPI_COMBINER_HINDEXED_INTEGER:{
MPI_Aint tmpPrevExtent;
int count, blocklength;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("<");
count = array_of_ints[0];
for (i = 0; i < count; i++) { // count
blocklength = array_of_ints[i + 1]; // array of blocklength
tmpPrevExtent = prevExtentTot;
tmpPrevExtent += array_of_adds[i]; // + displacement in byte
printf( "BL : %d - ", blocklength);
for (j = 0; j < blocklength; j++) {
tmpPrevExtent = printdatatype( array_of_dtypes[0], tmpPrevExtent);
printf(", ");
tmpPrevExtent += subExtent;
}
}
printf(">, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_INDEXED_BLOCK:{
MPI_Aint tmpPrevExtent;
int count;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("<");
count = array_of_ints[0];
for (i = 0; i < count; i++) { // count
tmpPrevExtent = prevExtentTot;
tmpPrevExtent += array_of_ints[i + 2] * subExtent; // + displacement * size of block
printf( "BL : %d - ", array_of_ints[i + 1]);
for (j = 0; j < array_of_ints[1]; j++) { // blocklength
tmpPrevExtent = printdatatype( array_of_dtypes[0], tmpPrevExtent);
printf(", ");
tmpPrevExtent += subExtent;
}
}
printf(">, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_STRUCT:
case MPI_COMBINER_STRUCT_INTEGER:{
MPI_Aint tmpPrevExtent;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
printf( "{");
for (i = 0; i < array_of_ints[0]; i++) { // count
tmpPrevExtent = prevExtentTot + array_of_adds[i]; // origin + displacement
printf( "BL : %d - ", array_of_ints[i + 1]);
tmpPrevExtent = printdatatype( array_of_dtypes[i], tmpPrevExtent);
tmpPrevExtent += subExtent;
printf(", ");
}
printf("}, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_SUBARRAY:
// I don't know what is interresting to display here...
printf("... subarray not handled ...");
break;
case MPI_COMBINER_DARRAY:
// Same
printf("... darray not handled ...");
break;
case MPI_COMBINER_RESIZED:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", \n");
break;
default:
printf( "Unrecognized combiner type\n" );
}
free( array_of_ints );
free( array_of_adds );
free( array_of_dtypes );
return prevExtentTot;
}
void printMapDatatype(MPI_Datatype datatype) {
MPI_Aint lb, ub;
MPI_Type_lb(datatype, &lb);
MPI_Type_ub(datatype, &ub);
printf("\"(LB, %ld), ", lb);
printdatatype(datatype, 0);
printf("(UB, %ld)\"\n", ub);
}
我同意“类型映射”和“类型签名”对于调试和理解非常有用。我们可能可以在 MPICH 中提供它 -- https://github.com/hzhou/mpich/pull/10。但我不确定公开它的正确接口。返回一个字符串,可能很长,只是让用户解析字符串似乎不是一个明智的选择。
type map
是 MPI 中一个重要但令人困惑的概念。我想要一个例程来为我显示或打印类型图。
例如(取自 MPI-3 标准),
MPI_TYPE_CREATE_RESIZED(MPI_INT, -3, 9, type1)
类型映射中的结果
{(lb_marker, -3), (int, 0), (ub_marker, 6)}.
再次使用该类型:
MPI_TYPE_CONTIGUOUS(2, type1, type2)
类型映射是
{(lb_marker, -3), (int, 0), (int,9), (ub_marker, 15)}
我想要一种自动显示该类型图的方法。
当然可以使用 MPI_Type_get_contents
和 MPI_Type_get_envelope
并递归下降直到找到内置类型。这是一个巨大的痛苦,我本以为会有 20 年的时间来使用某种工具来为我做这件事。
一些很有前途但效果不佳的工具:
我从 ~ 2001 here 找到了 MPImap。首先,它需要针对现代 Tcl/TK 进行更新,修补以解决一些内存错误,然后再进行更新;您会得到一个无响应的 GUI。相反,我正在寻找可以在 运行 时间调用的 library/routine。
MPIDU_Datatype_deubg
是 MPICH 特定的内部类型转储例程。它不显示类型映射(它显示数据循环表示,再次关闭)曾经有一个名为 XMPI 的调试器,在其功能中列出了显示 MPI 类型映射的能力。这个调试器似乎是特定于 LAM-MPI 的,并且没有使用 get_contents/ get_envelope.
As Rob Latham said, there isn't good pre-existing solutions. With the help of the links given by tim I created this function available on Github. I took your example for the contiguous + resize test (here) 输出为
contiguous + resize
"(LB, -3), (MPI_INT, 0), (MPI_INT, 9), (UB, 15)"
有了这个功能你只需要做printMapDatatype(mydatatype)
。我希望这就是您要搜索的内容。
这是函数,以防万一:
MPI_Aint printdatatype( MPI_Datatype datatype, MPI_Aint prevExtentTot ) {
int *array_of_ints;
MPI_Aint *array_of_adds;
MPI_Datatype *array_of_dtypes;
int num_ints, num_adds, num_dtypes, combiner;
int i, j;
MPI_Type_get_envelope( datatype, &num_ints, &num_adds, &num_dtypes, &combiner );
array_of_ints = (int *) malloc( num_ints * sizeof(int) );
array_of_adds = (MPI_Aint *) malloc( num_adds * sizeof(MPI_Aint) );
array_of_dtypes = (MPI_Datatype *) malloc( num_dtypes * sizeof(MPI_Datatype) );
MPI_Aint extent, subExtent;
MPI_Type_extent(datatype, &extent);
switch (combiner) {
case MPI_COMBINER_NAMED:
// To print the specific type, we can match against the predefined forms.
if (datatype == MPI_BYTE) printf( "(MPI_BYTE, %ld)", prevExtentTot);
else if (datatype == MPI_LB) printf( "(MPI_LB, %ld)", prevExtentTot);
else if (datatype == MPI_PACKED) printf( "(MPI_PACKED, %ld)", prevExtentTot);
else if (datatype == MPI_UB) printf( "(MPI_UB, %ld)", prevExtentTot);
else if (datatype == MPI_CHAR) printf( "(MPI_CHAR, %ld)", prevExtentTot);
else if (datatype == MPI_DOUBLE) printf( "(MPI_DOUBLE, %ld)", prevExtentTot);
else if (datatype == MPI_FLOAT) printf( "(MPI_FLOAT, %ld)", prevExtentTot);
else if (datatype == MPI_INT) printf( "(MPI_INT, %ld)", prevExtentTot );
else if (datatype == MPI_LONG) printf( "(MPI_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_LONG_DOUBLE) printf( "(MPI_LONG_DOUBLE, %ld)", prevExtentTot);
else if (datatype == MPI_LONG_LONG) printf( "(MPI_LONG_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_LONG_LONG_INT) printf( "(MPI_LONG_LONG_INT, %ld)", prevExtentTot);
else if (datatype == MPI_SHORT) printf( "(MPI_SHORT, %ld)", prevExtentTot);
else if (datatype == MPI_SIGNED_CHAR) printf( "(MPI_SIGNED_CHAR, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED) printf( "(MPI_UNSIGNED, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_CHAR) printf( "(MPI_UNSIGNED_CHAR, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_LONG) printf( "(MPI_UNSIGNED_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_LONG_LONG)printf( "(MPI_UNSIGNED_LONG_LONG, %ld)", prevExtentTot);
else if (datatype == MPI_UNSIGNED_SHORT) printf( "(MPI_UNSIGNED_SHORT, %ld)", prevExtentTot);
else if (datatype == MPI_WCHAR) printf( "(MPI_WCHAR, %ld)", prevExtentTot);
free( array_of_ints );
free( array_of_adds );
free( array_of_dtypes );
return prevExtentTot;
break;
case MPI_COMBINER_DUP:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", \n");
break;
case MPI_COMBINER_CONTIGUOUS:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
for (i=0; i < array_of_ints[0]; i++) {
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
prevExtentTot += subExtent;
printf(", ");
}
break;
case MPI_COMBINER_VECTOR:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("[");
for (i = 0; i < array_of_ints[0]; i++) { //count
printf( "BL : %d - ", array_of_ints[1]);
for (j = 0; j < array_of_ints[2]; j++) { // stride
if (j < array_of_ints[1]) { // if in blocklength
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", ");
}
prevExtentTot += subExtent;
}
}
printf("], ");
break;
case MPI_COMBINER_HVECTOR:
case MPI_COMBINER_HVECTOR_INTEGER:{
MPI_Aint backupPrevExtent = prevExtentTot;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("[");
for (i = 0; i < array_of_ints[0]; i++) { //count
printf( "BL : %d - ", array_of_ints[1]);
for (j = 0; j < array_of_ints[1]; j++) { // blocklength
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", ");
prevExtentTot += subExtent;
}
prevExtentTot = backupPrevExtent + array_of_adds[0]; // + stride un byte
}
printf("], ");
break;
}
case MPI_COMBINER_INDEXED:{
MPI_Aint tmpPrevExtent;
int count, blocklength;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("<");
count = array_of_ints[0];
for (i = 0; i < count; i++) { // count
blocklength = array_of_ints[i + 1]; // array of blocklength
tmpPrevExtent = prevExtentTot;
tmpPrevExtent += array_of_ints[count + 1 + i] * subExtent; // + displacement * size of block
printf( "BL : %d - ", blocklength);
for (j = 0; j < blocklength; j++) { // blocklength
tmpPrevExtent = printdatatype( array_of_dtypes[0], tmpPrevExtent);
printf(", ");
tmpPrevExtent += subExtent;
}
}
printf(">, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_HINDEXED:
case MPI_COMBINER_HINDEXED_INTEGER:{
MPI_Aint tmpPrevExtent;
int count, blocklength;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("<");
count = array_of_ints[0];
for (i = 0; i < count; i++) { // count
blocklength = array_of_ints[i + 1]; // array of blocklength
tmpPrevExtent = prevExtentTot;
tmpPrevExtent += array_of_adds[i]; // + displacement in byte
printf( "BL : %d - ", blocklength);
for (j = 0; j < blocklength; j++) {
tmpPrevExtent = printdatatype( array_of_dtypes[0], tmpPrevExtent);
printf(", ");
tmpPrevExtent += subExtent;
}
}
printf(">, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_INDEXED_BLOCK:{
MPI_Aint tmpPrevExtent;
int count;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
MPI_Type_extent(array_of_dtypes[0], &subExtent); // no need to do in loop because same type
printf("<");
count = array_of_ints[0];
for (i = 0; i < count; i++) { // count
tmpPrevExtent = prevExtentTot;
tmpPrevExtent += array_of_ints[i + 2] * subExtent; // + displacement * size of block
printf( "BL : %d - ", array_of_ints[i + 1]);
for (j = 0; j < array_of_ints[1]; j++) { // blocklength
tmpPrevExtent = printdatatype( array_of_dtypes[0], tmpPrevExtent);
printf(", ");
tmpPrevExtent += subExtent;
}
}
printf(">, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_STRUCT:
case MPI_COMBINER_STRUCT_INTEGER:{
MPI_Aint tmpPrevExtent;
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
printf( "{");
for (i = 0; i < array_of_ints[0]; i++) { // count
tmpPrevExtent = prevExtentTot + array_of_adds[i]; // origin + displacement
printf( "BL : %d - ", array_of_ints[i + 1]);
tmpPrevExtent = printdatatype( array_of_dtypes[i], tmpPrevExtent);
tmpPrevExtent += subExtent;
printf(", ");
}
printf("}, ");
prevExtentTot = tmpPrevExtent;
break;
}
case MPI_COMBINER_SUBARRAY:
// I don't know what is interresting to display here...
printf("... subarray not handled ...");
break;
case MPI_COMBINER_DARRAY:
// Same
printf("... darray not handled ...");
break;
case MPI_COMBINER_RESIZED:
MPI_Type_get_contents( datatype, num_ints, num_adds, num_dtypes, array_of_ints, array_of_adds, array_of_dtypes );
prevExtentTot = printdatatype( array_of_dtypes[0], prevExtentTot);
printf(", \n");
break;
default:
printf( "Unrecognized combiner type\n" );
}
free( array_of_ints );
free( array_of_adds );
free( array_of_dtypes );
return prevExtentTot;
}
void printMapDatatype(MPI_Datatype datatype) {
MPI_Aint lb, ub;
MPI_Type_lb(datatype, &lb);
MPI_Type_ub(datatype, &ub);
printf("\"(LB, %ld), ", lb);
printdatatype(datatype, 0);
printf("(UB, %ld)\"\n", ub);
}
我同意“类型映射”和“类型签名”对于调试和理解非常有用。我们可能可以在 MPICH 中提供它 -- https://github.com/hzhou/mpich/pull/10。但我不确定公开它的正确接口。返回一个字符串,可能很长,只是让用户解析字符串似乎不是一个明智的选择。