FFTW MPI高级接口1D实复变换段错误问题求助
FFTW MPI实-复/复-实批量变换段错误问题排查
问题描述
使用FFTW高级接口结合MPI批量执行1D傅里叶变换,复-复变换运行正常,但切换为实-复(r2c)和复-实(c2r)变换后出现段错误。已调整变换计划和执行函数,但问题依旧。怀疑复数数组元素分配数量有误(尝试过N/2和N/2+1两种方式),同时注意到错误信息中出现fftw_execute_r2r,但实际调用的是fftw_execute_dft_r2c,对此存在疑问。
代码与错误信息
代码
#include <stdio.h> #include <stdlib.h> #include <mpi.h> #include <unistd.h> #include <complex.h> #include <fftw3-mpi.h> int main(int argc, char *argv[]) { /* --- MPI Init --------------------------------------------------- */ int rank, size; int Nproc_x; int Nx, Ny; int nx = 3; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &Nproc_x); Nx = Nproc_x*nx; Ny = 40; /* --- FFTW INITIALISATION ----------------------------------------------- */ int i, N; int fftw_rank; ptrdiff_t fftw_dims[1]; ptrdiff_t howmany, block0; /* local data size */ ptrdiff_t fftw_alloc_local; ptrdiff_t fftw_local_nx; ptrdiff_t fftw_local_x_start; /* Complex arrays */ double * rho_real; fftw_complex * rho_cmplx; /* Plans for FFTW transforms */ static fftw_plan r2c_plan; static fftw_plan c2r_plan; /* Other variables */ static double FFT_norm; fftw_mpi_init(); fftw_rank = 1; fftw_dims[0] = Nx; howmany = Ny; block0 = nx; /* Array size in radial dir on current processor */ FFT_norm = (double) 1/Nx; /* get local data size and allocate data array */ fftw_alloc_local = fftw_mpi_local_size_many(fftw_rank, fftw_dims, howmany, block0, MPI_COMM_WORLD, /* Transform in azimuth */ &fftw_local_nx, &fftw_local_x_start); N = fftw_alloc_local; rho_real = fftw_alloc_real(N); rho_cmplx = fftw_alloc_complex(N/2+1); /* plan for forward transformation: real density to complex density */ r2c_plan = fftw_mpi_plan_many_dft_r2c(fftw_rank, fftw_dims, howmany, block0, FFTW_MPI_DEFAULT_BLOCK, rho_real, rho_cmplx, MPI_COMM_WORLD, FFTW_ESTIMATE); c2r_plan = fftw_mpi_plan_many_dft_c2r(fftw_rank, fftw_dims, howmany, FFTW_MPI_DEFAULT_BLOCK, block0, rho_cmplx, rho_real, MPI_COMM_WORLD, FFTW_ESTIMATE); /* Populate real array */ for (int i = 0; i < fftw_local_nx; ++i) { for (int j = 0; j < Ny; ++j) { rho_real[i * Ny + j] = i * Ny + j ; } } fftw_mpi_execute_dft_r2c(r2c_plan, rho_real, rho_cmplx); fftw_mpi_execute_dft_c2r(c2r_plan, rho_cmplx, rho_real); printf("INFO(gravBessel): fftw execute success ! \n"); MPI_Finalize(); return 0; }
错误信息
[euler:12536] *** Process received signal *** [euler:12536] Signal: Segmentation fault (11) [euler:12536] Signal code: Address not mapped (1) [euler:12536] Failing at address: (nil) [euler:12536] [ 0] /lib/x86_64-linux-gnu/libc.so.6(+0x43090)[0x7f2f7ff04090] [euler:12536] [ 1] /lib/x86_64-linux-gnu/libfftw3.so.3(fftw_execute_r2r+0x4)[0x7f2f802e59d4] [euler:12536] [ 2] my_program(+0x1509)[0x55da6283a509] [euler:12536] [ 3] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0x7f2f7fee5083] [euler:12536] [ 4] my_program(+0x122e)[0x55da6283a22e] [euler:12536] *** End of error message ***
问题根源与修复方案
1. 复数数组分配错误
fftw_mpi_local_size_many仅适用于复-复变换的本地大小计算,实-复/复-实变换的复数数组需要使用专用函数fftw_mpi_local_size_many_dft_r2c计算正确的本地元素数量——N/2+1是全局复数数组的大小逻辑,不适用于MPI分布式场景,直接推导会导致数组空间不足或冗余,触发越界访问。
修复代码:
// 替换原复数数组分配部分 ptrdiff_t fftw_local_nx_cmplx, fftw_local_x_start_cmplx; ptrdiff_t fftw_alloc_local_cmplx = fftw_mpi_local_size_many_dft_r2c( fftw_rank, fftw_dims, howmany, block0, FFTW_MPI_DEFAULT_BLOCK, MPI_COMM_WORLD, &fftw_local_nx_cmplx, &fftw_local_x_start_cmplx ); rho_cmplx = fftw_alloc_complex(fftw_alloc_local_cmplx);
2. 数组初始化索引错误
原代码中数组初始化的索引顺序rho_real[i * Ny + j]颠倒了变换维度与批量维度的存储顺序。FFTW的many类变换采用批量优先的存储布局:每个批量(howmany)的完整变换数据连续存储,即第j个批量的第i个本地元素应为rho_real[j * fftw_local_nx + i]。原索引会导致数组越界写入,触发段错误。
修复代码:
/* Populate real array (修正索引顺序) */ for (int j = 0; j < Ny; ++j) { for (int i = 0; i < fftw_local_nx; ++i) { rho_real[j * fftw_local_nx + i] = j * fftw_local_nx + i; } }
3. 关于fftw_execute_r2r的疑问
错误信息中出现fftw_execute_r2r属于正常现象:FFTW内部会将实-复/复-实变换转换为实-实变换的特殊类型实现,无需额外处理,此并非问题根源。
4. 额外建议:添加计划创建校验
计划创建失败会返回NULL,直接调用执行函数会导致崩溃,建议添加校验逻辑:
if (!r2c_plan || !c2r_plan) { fprintf(stderr, "Rank %d: Failed to create FFTW plans\n", rank); MPI_Abort(MPI_COMM_WORLD, 1); }
修复后完整代码示例
#include <stdio.h> #include <stdlib.h> #include <mpi.h> #include <unistd.h> #include <complex.h> #include <fftw3-mpi.h> int main(int argc, char *argv[]) { /* --- MPI Init --------------------------------------------------- */ int rank, size; int Nproc_x; int Nx, Ny; int nx = 3; MPI_Init(&argc, &argv); MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &Nproc_x); Nx = Nproc_x*nx; Ny = 40; /* --- FFTW INITIALISATION ----------------------------------------------- */ int i; int fftw_rank; ptrdiff_t fftw_dims[1]; ptrdiff_t howmany, block0; /* local data size */ ptrdiff_t fftw_alloc_local; ptrdiff_t fftw_local_nx; ptrdiff_t fftw_local_x_start; /* Complex arrays */ double * rho_real; fftw_complex * rho_cmplx; /* Plans for FFTW transforms */ fftw_plan r2c_plan; fftw_plan c2r_plan; /* Other variables */ double FFT_norm; fftw_mpi_init(); fftw_rank = 1; fftw_dims[0] = Nx; howmany = Ny; block0 = nx; /* Array size in radial dir on current processor */ FFT_norm = (double) 1/Nx; /* get local data size for real array */ fftw_alloc_local = fftw_mpi_local_size_many(fftw_rank, fftw_dims, howmany, block0, MPI_COMM_WORLD, &fftw_local_nx, &fftw_local_x_start); rho_real = fftw_alloc_real(fftw_alloc_local); /* get local data size for complex array (r2c专用) */ ptrdiff_t fftw_alloc_local_cmplx; ptrdiff_t fftw_local_nx_cmplx; ptrdiff_t fftw_local_x_start_cmplx; fftw_alloc_local_cmplx = fftw_mpi_local_size_many_dft_r2c( fftw_rank, fftw_dims, howmany, block0, FFTW_MPI_DEFAULT_BLOCK, MPI_COMM_WORLD, &fftw_local_nx_cmplx, &fftw_local_x_start_cmplx ); rho_cmplx = fftw_alloc_complex(fftw_alloc_local_cmplx); /* plan for forward transformation: real density to complex density */ r2c_plan = fftw_mpi_plan_many_dft_r2c(fftw_rank, fftw_dims, howmany, block0, FFTW_MPI_DEFAULT_BLOCK, rho_real, rho_cmplx, MPI_COMM_WORLD, FFTW_ESTIMATE); c2r_plan = fftw_mpi_plan_many_dft_c2r(fftw_rank, fftw_dims, howmany, FFTW_MPI_DEFAULT_BLOCK, block0, rho_cmplx, rho_real, MPI_COMM_WORLD, FFTW_ESTIMATE); /* 校验计划是否创建成功 */ if (!r2c_plan || !c2r_plan) { fprintf(stderr, "Rank %d: Failed to create FFTW plans\n", rank); MPI_Abort(MPI_COMM_WORLD, 1); } /* Populate real array (修正索引顺序) */ for (int j = 0; j < Ny; ++j) { for (int i = 0; i < fftw_local_nx; ++i) { rho_real[j * fftw_local_nx + i] = j * fftw_local_nx + i; } } fftw_mpi_execute_dft_r2c(r2c_plan, rho_real, rho_cmplx); fftw_mpi_execute_dft_c2r(c2r_plan, rho_cmplx, rho_real); printf("INFO(gravBessel): fftw execute success ! Rank %d\n", rank); /* 释放资源 */ fftw_destroy_plan(r2c_plan); fftw_destroy_plan(c2r_plan); fftw_free(rho_real); fftw_free(rho_cmplx); MPI_Finalize(); return 0; }
内容的提问来源于stack exchange,提问作者steven
相关产品推荐
相关产品推荐

