MPI向MPI_Comm_spawn创建的进程组发送std::vector<string>问题求助
Hey there, let’s work through this problem— I’ve run into similar headaches when sending C++ containers over MPI, especially with spawned processes. The root issue is straightforward: MPI doesn’t natively recognize complex C++ types like std::vector<std::string>— it’s a C-based library, so it only knows about primitive data types and contiguous memory blocks. Sending an int works because it’s a single, fixed-size value, but vectors of strings have dynamic, non-contiguous memory under the hood, which breaks direct MPI transfers.
Here’s how to fix it, with step-by-step solutions:
1. Understand the Intercommunicator from MPI_Comm_spawn
First, make sure you’re using the intercommunicator correctly. When you call MPI_Comm_spawn, you get an intercomm that connects the parent process group to the spawned child group. For communication:
- The parent uses
MPI_ROOTas the source/destination rank when talking to the children. - Each child uses rank 0 (the parent’s rank in the intercomm) to talk back to the parent.
2. Serialize the Vector of Strings (Simplest Approach)
Since MPI can’t send the vector directly, we need to break it down into parts MPI understands: the size of the vector, then each string’s length and content.
Parent Process Code (Sending)
#include <mpi.h> #include <vector> #include <string> int main(int argc, char** argv) { MPI_Init(&argc, &argv); MPI_Comm intercomm; int spawn_count = 1; // Number of child processes to spawn MPI_Comm_spawn("./child_executable", MPI_ARGV_NULL, spawn_count, MPI_INFO_NULL, 0, MPI_COMM_WORLD, &intercomm, MPI_ERRCODES_IGNORE); // The vector we want to send std::vector<std::string> message = {"Hello", "from", "parent", "process!"}; int vec_size = message.size(); // Step 1: Send the size of the vector to the child MPI_Send(&vec_size, 1, MPI_INT, 0, 0, intercomm); // Step 2: Send each string's length and content for (const auto& str : message) { int str_len = str.size(); // Send string length first MPI_Send(&str_len, 1, MPI_INT, 0, 1, intercomm); // Send string content MPI_Send(str.c_str(), str_len, MPI_CHAR, 0, 2, intercomm); } MPI_Comm_free(&intercomm); MPI_Finalize(); return 0; }
Child Process Code (Receiving)
#include <mpi.h> #include <vector> #include <string> #include <iostream> int main(int argc, char** argv) { MPI_Init(&argc, &argv); MPI_Comm intercomm; MPI_Comm_get_parent(&intercomm); if (intercomm == MPI_COMM_NULL) { std::cerr << "No parent process found!" << std::endl; MPI_Abort(MPI_COMM_WORLD, 1); } int vec_size; // Step 1: Receive the vector size from parent MPI_Recv(&vec_size, 1, MPI_INT, MPI_ROOT, 0, intercomm, MPI_STATUS_IGNORE); std::vector<std::string> received(vec_size); // Step 2: Receive each string for (int i = 0; i < vec_size; ++i) { int str_len; MPI_Recv(&str_len, 1, MPI_INT, MPI_ROOT, 1, intercomm, MPI_STATUS_IGNORE); // Allocate buffer for string content std::vector<char> str_buf(str_len); MPI_Recv(str_buf.data(), str_len, MPI_CHAR, MPI_ROOT, 2, intercomm, MPI_STATUS_IGNORE); // Convert buffer to std::string received[i] = std::string(str_buf.begin(), str_buf.end()); } // Verify the received data std::cout << "Child received: "; for (const auto& str : received) { std::cout << str << " "; } std::cout << std::endl; MPI_Comm_free(&intercomm); MPI_Finalize(); return 0; }
3. Optimize with MPI_Pack/MPI_Unpack (Fewer Messages)
If you want to reduce the number of MPI communication calls, you can pack all the data into a single buffer first, then send it in one go. This is better for large datasets.
Parent Process (Packing & Sending)
// ... (setup code same as before) std::vector<std::string> message = {"Hello", "from", "parent", "process!"}; int vec_size = message.size(); // Calculate required buffer size for packing int pack_size = 0; MPI_Pack_size(1, MPI_INT, intercomm, &pack_size); for (const auto& str : message) { int str_len = str.size(); MPI_Pack_size(1, MPI_INT, intercomm, &pack_size); MPI_Pack_size(str_len, MPI_CHAR, intercomm, &pack_size); } // Allocate packing buffer std::vector<char> pack_buf(pack_size); int position = 0; // Pack vector size MPI_Pack(&vec_size, 1, MPI_INT, pack_buf.data(), pack_size, &position, intercomm); // Pack each string's length and content for (const auto& str : message) { int str_len = str.size(); MPI_Pack(&str_len, 1, MPI_INT, pack_buf.data(), pack_size, &position, intercomm); MPI_Pack(str.c_str(), str_len, MPI_CHAR, pack_buf.data(), pack_size, &position, intercomm); } // Send the packed buffer MPI_Send(pack_buf.data(), position, MPI_PACKED, 0, 0, intercomm); // ... (cleanup code same as before)
Child Process (Unpacking & Receiving)
// ... (setup code same as before) MPI_Status status; int recv_size; // Probe to get the size of the incoming packed message MPI_Probe(MPI_ROOT, 0, intercomm, &status); MPI_Get_count(&status, MPI_PACKED, &recv_size); // Allocate buffer to receive packed data std::vector<char> pack_buf(recv_size); MPI_Recv(pack_buf.data(), recv_size, MPI_PACKED, MPI_ROOT, 0, intercomm, MPI_STATUS_IGNORE); int position = 0; int vec_size; // Unpack vector size MPI_Unpack(pack_buf.data(), recv_size, &position, &vec_size, 1, MPI_INT, intercomm); std::vector<std::string> received(vec_size); // Unpack each string for (int i = 0; i < vec_size; ++i) { int str_len; MPI_Unpack(pack_buf.data(), recv_size, &position, &str_len, 1, MPI_INT, intercomm); std::vector<char> str_buf(str_len); MPI_Unpack(pack_buf.data(), recv_size, &position, str_buf.data(), str_len, MPI_CHAR, intercomm); received[i] = std::string(str_buf.begin(), str_buf.end()); } // ... (verify and cleanup code same as before)
Key Pitfalls to Avoid
- Never send the vector/string directly:
std::vectorstores a pointer to its data, andstd::stringdoes the same. Sending these pointers across processes is useless— the child process will get an invalid memory address from its own address space. - Always use matching tags: Make sure the tag values in
MPI_SendandMPI_Recvmatch (e.g., tag 0 for vector size, tag 1 for string lengths, etc.). - Check MPI return codes: Add error checking to
MPI_*calls to catch issues like invalid communicators or mismatched data types.
内容的提问来源于stack exchange,提问作者Adrian

