【问题标题】:MPI: program works depending on the number of processesMPI:程序的工作取决于进程的数量
【发布时间】:2014-02-19 13:50:41
【问题描述】:

我编写了一个使用 MPI 库 (MPICH-2) 的程序。该程序会找到 2 到 N 之间的所有素数。如果我只使用两个进程,它可以正常工作,但是当我指定进程数 >2 时,它就不行了。 该程序效率非常低,因为我的目标是比较使用 MPI 的程序和不使用 MPI 的同一程序之间的性能。 我有一台双核机器,我在 Ubuntu 13.10 上使用 NetBeans 7.4。问题是否取决于我机器的核心数量?我知道调度取决于操作系统,但我不知道该怎么想!也许我必须放置一个 MPI_Barrier?我在下面插入代码和输出,当我使用超过 2 个 proc 调用程序时,您会看到一些奇怪的零出现。

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>


int count(int *a, int N){
    int b[N];
    int i, j, n_prime;

    for(i=0; i<N; i++)
        b[i] = a[i];

    b[0] = b[1] = 0;
    for (i=2; i<N; i++) {
        b[i] = 1;
        for (j=2; j<i; j++) 
            if (i % j == 0) {
                    b[i] = 0; 
                    break;
            }
        }
    n_prime = 0;
    for (j=2; j<N; j++) 
    if (b[j]) n_prime++;
    return n_prime;
    }

int main(int argc, char** argv) {

    int size, rank, div;
    int N;
    int *array;
    int i, j, k, check, n_prime, n_prime_loc;
    int *sub_array, *prime, *recv_prime, *b, *prime2;
    double t1, t2;


    if(argc != 2){
    printf("Argument error: %s not recognized as argument\n", argv[0]);
    return -1;
    }

    N = atoi(argv[1]);

    MPI_Init(NULL, NULL);

    t1 = MPI_Wtime();

    MPI_Comm_size(MPI_COMM_WORLD, &size);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    div = N/size;       //elements per process

    b = (int*) malloc(sizeof(int)*N);
    if (b == NULL){
        printf("Cannot allocate array 'b'\n");
        return -1;
    }

    n_prime = count(b, N);

    /* Dynamic allocation of the arrays */
    sub_array = (int*) malloc(sizeof(int)*div);
    if (sub_array == NULL){
        printf("Cannot allocate array 'sub_array'\n");
        return -1;
    }
    recv_prime = (int*) malloc(sizeof(int)*n_prime);
    if (recv_prime == NULL){
        printf("Cannot allocate array 'recv_prime'\n");
        return -1;
    }
    array = (int*) malloc(sizeof(int)*N);
    if (array == NULL){
        printf("Cannot allocate array 'array'\n");
        return -1;
    }
    prime = (int*) malloc(sizeof(int)*n_prime);
    if (prime == NULL){
        printf("Cannot allocate array 'prime'\n");
        return -1;
    }

    /* Initialization of the array */
    for (i=0; i<N; i++)       
        array[i] = i+1;
    for(i=0; i<n_prime; i++)
        prime[i] = 0;
    for(i=0; i<n_prime; i++)
        recv_prime[i] = 0;

    /* Process 0 divides the array among the processes */
    MPI_Scatter(array, div, MPI_INT, sub_array, div, MPI_INT, 0, MPI_COMM_WORLD);

    check = 0;      //used to check if we find a divisor
    k=0;

    for(i=0; i<div; i++){
        check = 0;
        if (sub_array[i] == 1) continue;
        for(j=2; j<sub_array[i]; j++){
            if(sub_array[i] % j == 0){
                check = 1;
            }
        }
        if (check == 0){     //if we don't find a divisor, the number is prime
            prime[k] = sub_array[i];
            k++;
        }
    }

    n_prime_loc = 0;

    for(i=0; i<n_prime; i++)
        if(prime[i]!=0)
            n_prime_loc++;

    prime2 = (int*) malloc(sizeof(int)*n_prime_loc);

    j=0;
    for(i=0; i<n_prime; i++){
        if(prime[i]==0) continue;
        prime2[j] = prime[j];
        j++;
    }

    /* Each process sends its computation to the root process */
    MPI_Gather(prime2, n_prime_loc, MPI_INT, recv_prime, n_prime_loc, MPI_INT, 0, MPI_COMM_WORLD);

    MPI_Barrier(MPI_COMM_WORLD);

    if(rank == 0){

        printf("Prime numbers: ");
        for(i=0; i<n_prime; i++)
            printf("%i ", recv_prime[i]);
        printf("\n");
    }

    /* Free the allocated arrays */
    free(b);
    free(array);
    free(recv_prime);
    free(prime);
    free(prime2);
    free(sub_array);

    t2 = MPI_Wtime();
    //printf("Computation time for Process %i: %f\n", rank, t2-t1);

    MPI_Finalize();

    return (EXIT_SUCCESS);
    }

输出是:

hino@hino-X51L:~/NetBeansProjects/Prime$ mpiexec -np 10 ./Prime 1000
Prime numbers: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199
0 0 0 0 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 
317 331 337 347 349 353 359 367 373 379 383 389 397 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 
523 541 547 557 563 569 571 577 587 593 599 450 451 452 601 607 613 617 619 631 641 
643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 
787 797 649 650 485 486 
hino@hino-X51L:~/NetBeansProjects/Prime$ mpiexec -np 2 ./Prime 1000
Prime numbers: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 
211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 
337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 
461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 
601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 
739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 
881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 

【问题讨论】:

    标签: c mpi


    【解决方案1】:

    快速扫描显示这里有一些问题:

    1. 您需要确保您的数字数组平均分配。例如,如果您将 100 个数字分配给 3 个进程,那么您将有一些数字最终不会被 MPI_SCATTER 正确分配。不过这不是你的问题。

    2. 您的MPI_GATHER 在接收方有问题。这里的问题是您从每个进程发送n_prime_loc 数字,但每个进程的值也不同。例如,等级 0 可能会找到 10 个素数,因此其 n_prime_loc 的值为 10,但在等级 1 上,可能有 25 个素数(我知道这些示例不准确,但请坚持我)。问题是,由于等级 0 是 MPI_GATHER 操作的根,它的值 n_prime_loc 将被使用,这意味着如果另一个进程正在发送更多的等级 0 所期望的数字,就会出现问题。您需要生成一个足够大的缓冲区来保存所有可能的结果并适当地收集结果。或者,您可以选择不使用MPI_GATHER,因为缓冲区的大小是先验未知的。相反,您可以使用MPI_SENDMPI_RECV 并且可以使用MPI_PROBE 来了解缓冲区的大小。你可以让任何一个工作。

    3. 你的MPI_BARRIER在这里没用。出于某种原因,当人们不知道在 MPI 中做什么时,他们会抛出 MPI_BARRIER。这个函数所做的只是让所有进程在这一点上等待,直到所有其他进程都到达同一点。您已经通过调用 MPI_GATHER 完成了该操作,因为该调用涉及所有其他进程。

    4. 听起来您只在一个进程上运行此应用程序。虽然您当然可以做到这一点,但您不会真正获得太多加速,而且您不会获得比拥有内核更多的加速。如果您有一个双核处理器并且您尝试运行 4 个进程,您将花费大量时间来争夺 CPU 和上下文切换进出。您应该尽量不要使用比您拥有的内核更多的 MPI 等级。

    【讨论】:

    • 感谢您的帮助。对于第一点,我已经知道有这个问题,我必须尝试使用​​MPI_SCATTERV,似乎可以解决问题。您的第三点证实了我对使用 MPI_BARRIER 的怀疑。您的第二点清楚地说明了我的程序的真正问题是什么。所以你建议我最多使用 2 个进程来避免花费太多资源?第二个问题,如果我将 MPI_PROBE(如您所建议的)与 MPI_GATHER 一起使用会怎样?
    • 1.是的,如果你只有两个核心,使用超过 2 个 MPI 等级将无济于事。
    • 2. MPI_PROBE 仅适用于 MPI_RECV。它不适用于集体行动。查看文档 (mcs.anl.gov/research/projects/mpi/www/www3/MPI_Probe.html) 了解更多信息。
    • 非常感谢,我去看看文档,你的回答让我受益匪浅!
    猜你喜欢
    • 2014-10-12
    • 2013-02-08
    • 1970-01-01
    • 2015-02-20
    • 2011-06-28
    • 2014-06-07
    • 1970-01-01
    • 2022-01-05
    • 2018-02-28
    相关资源
    最近更新 更多