【问题标题】:MPI - no speedup with increasing amounts of processesMPI - 随着进程数量的增加没有加速
【发布时间】:2015-08-11 07:52:15
【问题描述】:

我正在编写测试数字是否为素数的程序。一开始我会计算分配给每个进程的数量,然后将这个数量发送给进程。接下来,执行计算并将数据发送回保存结果的进程 0。下面的代码有效,但是当我增加进程数量时,我的程序不会加速。在我看来,我的程序不能并行工作。怎么了?这是我在 MPI 的第一个程序,欢迎任何建议。

我使用 mpich2 在 Intel Core i7-950 上测试我的程序。

main.cpp:

if (rank == 0) {
    int workers = (size-1);
    readFromFile(path);
    int elements_per_proc = (N + (workers-1)) / workers;
    int rest = N % elements_per_proc;

    for (int i=1; i <= workers; i++) {
        if((i == workers) && (rest != 0))
            MPI_Send(&rest, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
        else
            MPI_Send(&elements_per_proc, 1, MPI_INT, i, 0, MPI_COMM_WORLD);
    }

    int it = 1;
    for (int i=0; i < N; i++) {
        if((i != 0) && ((i % elements_per_proc) == 0))
        it++;
        MPI_Isend(&input[i], 1, MPI_INT, it, 0, MPI_COMM_WORLD, &send_request);
    }
}

if (rank != 0) {
    int count;
    MPI_Recv(&count, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
    for (int j=0; j < count; j++) {
        MPI_Recv(&number, 1, MPI_INT, 0, MPI_ANY_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        result = test(number, k);
        send_array[0] = number;
        send_array[1] = result;
        MPI_Send(send_array, 2, MPI_INT, 0, 0, MPI_COMM_WORLD);
    }
}   

if (rank == 0) {
    for (int i=0; i < N; i++) {
        MPI_Recv(rec_array, 2, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
        //  save results
    }
}

【问题讨论】:

  • 您的主要测试算法是顺序的吗?
  • @Dieter Lücking,我使用 Miller-Rabin 素数测试。每个进程测试部分数字,这些操作是独立的。

标签: c++ performance mpi


【解决方案1】:

您的实施可能无法很好地扩展到许多流程,因为您在每个步骤中都进行了沟通。您当前为每个单一输入传达数字和结果,这会产生很大的延迟开销。相反,您应该考虑将输入 in-bulk 进行通信(即,使用单个消息)。

此外,使用 MPI 集合操作 (MPI_Scatter/MPI_Gather) 而不是 MPI_Send/MPI_Recv 的循环可能会进一步提高您的性能。

此外,您还可以利用 master 进程来处理大量输入。

一个更具可扩展性的实现可能如下所示:

// tell everybody how many elements there are in total
MPI_Bcast(&N, 1, MPI_INT, 0, MPI_COMM_WORLD);

// everybody determines how many elements it will work on
// (include the master process)
int num_local_elements = N / size + (N % size < rank ? 1 : 0);
// allocate local size
int* local_input = (int*) malloc(sizeof(int)*num_local_elements);

// distribute the input from master to everybody using MPI_Scatterv
int* counts; int* displs;
if (rank == 0) {
    counts = (int*)malloc(sizeof(int) * size);
    displs = (int*)malloc(sizeof(int) * size);
    for (int i = 0; i < size; i++) {
        counts[i] = N / size + (N % size < i ? 1 : 0);
        if (i > 0)
            displs[i] = displs[i-1] + counts[i-1];
    }
    // scatter from master
    MPI_Scatterv(input, counts, displs, MPI_INT, local_input, num_local_elements, MPI_INT, 0, MPI_COMM_WORLD);
} else {
    // receive scattered numbers
    MPI_Scatterv(NULL, NULL, NULL, MPI_DATATYPE_NULL, local_input, num_local_elements, MPI_INT, 0, MPI_COMM_WORLD);
}

// perform prime testing
int* local_results = (int*) malloc(sizeof(int)*num_local_elements);
for (int i = 0; i < num_local_elements; ++i) {
    local_results[i] = test(local_input[i], k);
}

// gather results back to master process
int* results;
if (rank == 0) {
    results = (int*)malloc(sizeof(int)*N);
    MPI_Gatherv(local_results, num_local_elements, MPI_INT, results, counts, displs, MPI_INT, 0, MPI_COMM_WORLD);
    // TODO: save results on master process
} else {
    MPI_Gatherv(local_results, num_local_elements, MPI_INT, NULL, NULL, NULL, MPI_INT, 0, MPI_COMM_WORLD);
}

【讨论】:

  • 非常感谢,您的实现很棒。我考虑过 MPI_Scatterv 和 MPI_Gatherv,但在这种情况下,数据由所有进程发送/接收(也是 0 级)。我想这样做 rank 0 不参与计算,只广播数据并保存结果。我可以做些什么来增加我的实施?
  • 您可以做两件事:1.) 发送问题并批量接收结果。这意味着您有两个沟通阶段。避免单独发送每个数字/结果。 2.) 编辑 scatterv/gatherv,使进程 0 的计数为 0,并且问题分布在 (size-1) 而不是 size 进程中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
  • 2018-09-22
  • 1970-01-01
  • 2017-05-27
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
相关资源
最近更新 更多