簡體   English   中英

如何使用 MPI 發送 GMP 或 MPFR 類型

[英]How to send GMP or MPFR types with MPI

我嘗試使用MPI_Scatter發送mpfr_t類型的變量。 例如:

mpfr_t *v1 = new mpfr_t[10];  
mpfr_t *v2 = new mpfr_t[10];   
MPI_Scatter(v1, 5, MPI_BYTE, v2, 5, MPI_BYTE, 0, MPI_COMM_WORLD ); 
for (int i = 0; i < 5; i++) 
    mpfr_printf("value rank %d -  %RNf \n", ProcRank, v2[i]);

它打印:

value rank 0 - nan
value rank 0 - nan
value rank 0 - nan
.....
value rank 1 - nan
value rank 0 - nan

但這是 MPI_Bcast 的工作。 我做錯了什么。 代碼 C/C++,MPI 庫是 OpenMPI-1.6。

您將發送計數指定為5,將數據類型指定為MPI_BYTE。 這似乎很奇怪。 如果要使用MPI_BYTE,並且要發送5個mpfr_t值,則將發送計數指定為5 * sizeof(mpfr_t)。 另一個選擇是創建自己的MPI派生數據類型(如果要擺脫sizeof())。

正如@LaryxDecidua 已經指出的那樣,MPFR 數字在堆上使用動態 memory,因此不能直接用於 MPI 操作。 但是,如果您可以使用 MPFR 4.0.0 或更高版本,則可以使用 MPFR function mpfr_fpif_export將數字序列化為線性 memory 緩沖區,然后使用 MPI 發送此緩沖區。 接收端可以通過調用mpfr_fpif_import恢復原來的號碼。 這兩個函數都對FILE *句柄進行操作,因此您還需要使用open_memstream和/或fmemopen將文件句柄 map 到 memory 緩沖區。 不幸的是,序列化數字的長度不僅取決於精度,還取決於值(例如,值 0 比其他數字占用更少的字節),因此像MPI_ScatterMPI_Gather這樣的組通信需要為每個等級固定緩沖區大小將不會'不工作。 請改用MPI_SendMPI_recv對。

這是一個完整的 C++17 示例,它為mpreal代碼庫提供了一個很好的 MPFR 數字接口:

#include <cstdio>
#include <iostream>
#include <mpi.h>
#include <mpreal.h>
#include <numeric>
#include <optional>


int main(int argc, char **argv) {
    MPI_Init(&argc, &argv);
    int world_rank;
    MPI_Comm_rank(MPI_COMM_WORLD, &world_rank);
    int world_size;
    MPI_Comm_size(MPI_COMM_WORLD, &world_size);


    std::vector<mpfr::mpreal> real_vec{world_rank, M_PI};

    // Serialize the mpreal vector to memory (send_buf)
    char *send_buf;
    size_t send_buf_size;
    FILE *real_stream = open_memstream(&send_buf, &send_buf_size);
    for (auto &real : real_vec) {
        mpfr_fpif_export(real_stream, real.mpfr_ptr());
    }
    fclose(real_stream);

    // Gather the buffer length of all processes
    std::optional<std::vector<size_t>> send_buf_size_vec;
    if (world_rank == 0) {
        send_buf_size_vec = std::vector<size_t>(world_size);
    }
    MPI_Gather(&send_buf_size, 1, MPI_UNSIGNED_LONG, (send_buf_size_vec ? send_buf_size_vec->data() : nullptr), 1,
               MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD);

    if (world_rank != 0) {
        // Send the serialized mpreal vector to rank 0
        MPI_Send(send_buf, send_buf_size, MPI_BYTE, 0, 0, MPI_COMM_WORLD);
    } else {
        // Create a recv buffer which can hold the data from all processes
        size_t recv_buf_size = std::accumulate(send_buf_size_vec->begin(), send_buf_size_vec->end(), 0UL);
        std::vector<char> recv_buf(recv_buf_size);
        auto all_buf_it = recv_buf.begin();

        // Directly copy the send buffer of process 0
        std::memcpy(&*all_buf_it, send_buf, send_buf_size);
        all_buf_it += (*send_buf_size_vec)[0];

        // Receive serialized numbers from all other ranks
        MPI_Status status;
        for (int i = 1; i < world_size; ++i) {
            MPI_Recv(&*all_buf_it, (*send_buf_size_vec)[i], MPI_BYTE, i, MPI_ANY_TAG, MPI_COMM_WORLD, &status);
            all_buf_it += (*send_buf_size_vec)[i];
        }

        // Import all mpreals from the receive buffer
        real_stream = fmemopen(recv_buf.data(), recv_buf.size(), "rb");
        std::vector<mpfr::mpreal> all_real_vec(world_size * real_vec.size());
        for (auto &real : all_real_vec) {
            mpfr_fpif_import(real.mpfr_ptr(), real_stream);
        }
        fclose(real_stream);

        // Print all received values
        std::cout << "Read values:" << std::endl;
        for (auto &real : all_real_vec) {
            std::cout << real << std::endl;
        }
    }

    MPI_Finalize();

    return 0;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM