创建动态大小的MPI文件视图:集体I/O派生类型动态数组实现
Great job getting the fixed-length u[10] case working! Moving to a dynamic-length u array requires adjusting how you define your MPI derived type, since MPI can't directly handle pointer members (they just store memory addresses, not the actual data). Here are two robust solutions tailored to your scenario (where all u arrays have the same length across processes):
Method 1: Use a Flexible Array Member (Cleanest & Most Compatible)
The simplest fix is to modify your struct to use a C99 flexible array member, which makes your int_data and u array live in a single contiguous block of memory. This aligns perfectly with how MPI expects derived types to be laid out.
Step 1: Update the Struct Definition
#include <stddef.h> // For offsetof() struct soln_dynamic_t { int int_data[2]; double u[]; // Flexible array member (no fixed length) };
Step 2: Allocate Memory for the Struct + Dynamic Array
When creating an instance, allocate memory to fit both the struct and your dynamic u array:
int N; // Shared dynamic length (all processes use the same N) // First, broadcast N to all processes (e.g., from rank 0) if (rank == 0) { N = 150; // Example dynamic length } MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD); // Allocate contiguous memory for the struct + u array struct soln_dynamic_t *my_soln = malloc(sizeof(struct soln_dynamic_t) + N * sizeof(double));
Step 3: Create the MPI Derived Type
Use MPI_Type_create_struct to define the layout, relying on offsetof() to handle memory alignment correctly (avoids bugs from compiler-specific padding):
MPI_Datatype soln_dynamic_type; int block_counts[2] = {2, N}; // 2 ints, N doubles MPI_Datatype block_types[2] = {MPI_INT, MPI_DOUBLE}; MPI_Aint displacements[2]; // Calculate offsets relative to the struct's base address displacements[0] = offsetof(struct soln_dynamic_t, int_data); displacements[1] = offsetof(struct soln_dynamic_t, u); // Create and commit the derived type MPI_Type_create_struct(2, block_counts, displacements, block_types, &soln_dynamic_type); MPI_Type_commit(&soln_dynamic_type);
Step 4: Write with Collective I/O
This works almost identically to your fixed-length case. Use MPI_Type_size to get the exact size of your derived type for file offsets:
MPI_File fh; MPI_File_open(MPI_COMM_WORLD, "output.bin", MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh); // Calculate each process's starting offset in the file int type_size; MPI_Type_size(soln_dynamic_type, &type_size); MPI_Offset offset = rank * type_size; // Set the file view to match our derived type MPI_File_set_view(fh, offset, soln_dynamic_type, soln_dynamic_type, "native", MPI_INFO_NULL); // Write the entire struct + array in one collective call MPI_File_write_all(fh, my_soln, 1, soln_dynamic_type, MPI_STATUS_IGNORE); // Cleanup MPI_File_close(&fh); MPI_Type_free(&soln_dynamic_type); free(my_soln);
Method 2: Keep the Original Struct (Use Scatter/Gather I/O)
If you can't modify the original struct (e.g., legacy code dependencies), you can use MPI_File_write_allv to write the two separate buffers (int_data and u) as a single logical record.
Step 1: Prepare Scatter/Gather Parameters
struct soln_dynamic_t my_soln; my_soln.u = malloc(N * sizeof(double)); // Populate my_soln.int_data and my_soln.u... // Define the two data blocks to write MPI_Datatype write_types[2] = {MPI_INT, MPI_DOUBLE}; int write_counts[2] = {2, N}; MPI_Aint write_displs[2]; // Calculate file offsets for each block (relative to process's starting position) int int_size, double_size; MPI_Type_size(MPI_INT, &int_size); MPI_Type_size(MPI_DOUBLE, &double_size); MPI_Offset base_offset = rank * (2 * int_size + N * double_size); write_displs[0] = base_offset; write_displs[1] = base_offset + 2 * int_size;
Step 2: Perform Collective Write
MPI_File fh; MPI_File_open(MPI_COMM_WORLD, "output.bin", MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &fh); // Set view to raw bytes since we're handling offsets manually MPI_File_set_view(fh, 0, MPI_BYTE, MPI_BYTE, "native", MPI_INFO_NULL); // Write both buffers in one collective call void *write_bufs[2] = {my_soln.int_data, my_soln.u}; MPI_File_write_allv(fh, write_bufs, write_counts, write_types, write_displs, MPI_STATUS_IGNORE); // Cleanup MPI_File_close(&fh); free(my_soln.u);
Key Notes
- Consistent N: Ensure all processes use the same
N(broadcast it if it's determined at runtime by one process). - Memory Alignment: Always use
offsetof()orMPI_Type_get_extentto calculate offsets—never hardcode sizes (compilers add padding to structs). - Type Cleanup: Remember to free your MPI derived types with
MPI_Type_freewhen done.
内容的提问来源于stack exchange,提问作者Donna

