含mpfr_t的自定义MPI结构体传输问题求助
MPFR结合MPI传输自定义结构体的错误修复
问题背景
将程序迁移到任意精度计算(使用MPFR库)时,原有的自定义MPI结构体需要替换为包含mpfr_t类型的字段。最初尝试直接用MPI_Type_create_struct定义结构体类型,但因mpfr_t的精度未固定,MPI无法确定其实际大小,导致传输失败。
后续改用MPI_Pack/MPI_Unpack处理,能正常传输int类型字段,但mpfr_t变量的导出、发送、接收、导入出现问题:前3个mpfr_t变量导入后为垃圾值,最后一个触发SIGABRT错误,断言信息为get_str.c:157: MPFR assertion failed: size_s1 >= m。
最初尝试的错误代码
typedef struct job_s { int f; mpfr_t l,t,r,b; } jobs; MPI_Datatype MPI_JOBS_TYPE; MPI_Type_create_struct( 5, (int []) {1, sizeof(mpfr_t), sizeof(mpfr_t), sizeof(mpfr_t), sizeof(mpfr_t)}, (MPI_Datatype []) {offsetof(jobs,f), offsetof(jobs,l), offsetof(jobs,t), offsetof(jobs,r), offsetof(jobs, b)}, (MPI_Datatype []) {MPI_INT, MPI_BYTE, MPI_BYTE, MPI_BYTE, MPI_BYTE}, &MPI_JOBS_TYPE ); MPI_Type_commit(&MPI_JOBS_TYPE);
出错的发送端代码
char * mpfr_buf; size_t mpfr_buf_size; char buf [3000]; pos = 0; MPI_Pack(&f, 1, MPI_INT, buf, 3000, &pos, MPI_COMM_WORLD); stream = open_memstream(&mpfr_buf, &mpfr_buf_size); for(i = 0; i < 4; ++i) mpfr_fpif_export(stream, bounds[i]); MPI_Pack(stream, 3000-sizeof(int), MPI_BYTE, buf, 3000, &pos, MPI_COMM_WORLD); print_bounds(bounds, "s"); MPI_Send(buf, pos, MPI_PACKED, reqRank, 0, MPI_COMM_WORLD); fclose(stream); f += 1;
出错的接收端代码
pos = 0; char buf [3000]; MPI_Recv(buf, 3000, MPI_PACKED, PARENT, 0, MPI_COMM_WORLD, &status); int msgSize = 0; MPI_Get_count(&status, MPI_BYTE, &msgSize); printf("msg size %d\n", msgSize); MPI_Unpack(buf, 3000, &pos, &f, 1, MPI_INT, MPI_COMM_WORLD); printf("Worker %d recived frame %d\n", rank, f); char streambuf [3000]; MPI_Unpack(buf, 3000, &pos, streambuf, msgSize - pos, MPI_BYTE, MPI_COMM_WORLD); stream = fmemopen(streambuf, 3000, "r"); for(i = 0; i < 4; ++i){ mpfr_fpif_import(bounds[i], stream); } fclose(stream);
错误原因及修复方案
核心错误点
- 发送端打包对象错误:试图打包文件流指针
stream而非实际导出的MPFR数据缓冲区mpfr_buf,且未关闭流就获取数据大小,导致mpfr_buf_size不准确。 - 接收端消息大小获取错误:
MPI_Get_count使用MPI_BYTE类型,而发送的是MPI_PACKED类型,导致消息大小计算有误。 - 解包缓冲区范围错误:
MPI_Unpack传入的总缓冲区大小为固定的3000,而非实际接收的msgSize,可能读取垃圾数据。 - 内存流初始化错误:
fmemopen使用固定长度3000,而非实际MPFR数据长度,导致MPFR解析时读到无效数据触发断言。
修复后的发送端代码
char *mpfr_buf = NULL; size_t mpfr_buf_size = 0; char buf[3000]; int pos = 0; // 打包int类型的f字段 MPI_Pack(&f, 1, MPI_INT, buf, 3000, &pos, MPI_COMM_WORLD); // 将MPFR变量导出到内存流 FILE *stream = open_memstream(&mpfr_buf, &mpfr_buf_size); if (!stream) { perror("open_memstream failed"); exit(EXIT_FAILURE); } for (int i = 0; i < 4; ++i) { if (mpfr_fpif_export(stream, bounds[i]) != 0) { perror("mpfr_fpif_export failed"); fclose(stream); free(mpfr_buf); exit(EXIT_FAILURE); } } // 关闭流后才能确保mpfr_buf_size是最终的实际数据大小 fclose(stream); // 打包MPFR导出的实际数据 MPI_Pack(mpfr_buf, mpfr_buf_size, MPI_BYTE, buf, 3000, &pos, MPI_COMM_WORLD); // 发送打包后的缓冲区 MPI_Send(buf, pos, MPI_PACKED, reqRank, 0, MPI_COMM_WORLD); // 释放内存流分配的缓冲区 free(mpfr_buf); f += 1;
修复后的接收端代码
int pos = 0; char buf[3000]; MPI_Status status; // 接收打包后的完整数据 MPI_Recv(buf, 3000, MPI_PACKED, PARENT, 0, MPI_COMM_WORLD, &status); int msgSize = 0; // 使用MPI_PACKED类型获取正确的消息大小 MPI_Get_count(&status, MPI_PACKED, &msgSize); printf("msg size %d\n", msgSize); // 解包int类型的f字段,传入实际消息大小作为缓冲区总长度 MPI_Unpack(buf, msgSize, &pos, &f, 1, MPI_INT, MPI_COMM_WORLD); printf("Worker %d received frame %d\n", rank, f); // 计算MPFR数据的实际长度 int mpfr_data_len = msgSize - pos; char streambuf[3000]; // 解包MPFR数据,使用实际消息大小作为缓冲区总长度 MPI_Unpack(buf, msgSize, &pos, streambuf, mpfr_data_len, MPI_BYTE, MPI_COMM_WORLD); // 以实际MPFR数据长度打开内存流,避免读取垃圾数据 FILE *stream = fmemopen(streambuf, mpfr_data_len, "r"); if (!stream) { perror("fmemopen failed"); exit(EXIT_FAILURE); } for (int i = 0; i < 4; ++i) { if (mpfr_fpif_import(bounds[i], stream) != 0) { perror("mpfr_fpif_import failed"); fclose(stream); exit(EXIT_FAILURE); } } fclose(stream);
内容的提问来源于stack exchange,提问作者Lucian Chauvin
相关产品推荐
相关产品推荐

