You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MPI向MPI_Comm_spawn创建的进程组发送std::vector<string>问题求助

Fixing MPI_Comm_spawn Communication with std::vectorstd::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_ROOT as 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::vector stores a pointer to its data, and std::string does 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_Send and MPI_Recv match (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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:09:33