Гибридный подход с OpenMP и MPI не использует одинаковое количество потоков в кластере с разным количеством хостов - PullRequest
0 голосов
/ 24 июня 2018

Я тестирую гибридный подход, выполняя параллель программы дружественных чисел (CAPBenchmark) с MPI и OpenMP.

В моем кластере 8 компьютеров, и на каждом компьютере установлен 4-ядерный процессор.

Код:

/*
 * Copyright(C) 2014 Pedro H. Penna <pedrohenriquepenna@gmail.com>
 * 
 * friendly-numbers.c - Friendly numbers kernel.
 */

#include <global.h>
#include <mpi.h>
#include <omp.h>
#include <stdio.h>
#include <stdlib.h>
#include <util.h>
#include "fn.h"

/*
 * Computes the Greatest Common Divisor of two numbers.
 */
static int gcd(int a, int b)
{
  int c;

  /* Compute greatest common divisor. */
  while (a != 0)
  {
     c = a;
     a = b%a;
     b = c;
  }

  return (b);
}

/*
 * Some of divisors.
 */
static int sumdiv(int n)
{
    int sum;    /* Sum of divisors. */
    int factor; /* Working factor.  */

    sum = 1 + n;

    /* Compute sum of divisors. */
    for (factor = 2; factor < n; factor++)
    {
        /* Divisor found. */
        if ((n%factor) == 0)
            sum += factor;
    }

    return (sum);
}

/*
 * Computes friendly numbers.
 */
int friendly_numbers(int start, int end) 
{
    int n;        /* Divisor.                    */
    int *num;     /* Numerator.                  */
    int *den;     /* Denominator.                */
    int *totalnum;
    int *totalden;
    int rcv_friends;
    int range;    /* Range of numbers.           */
    int i, j;     /* Loop indexes.               */
    int nfriends; /* Number of friendly numbers. */
    int slice;

    range = end - start + 1;
    slice = range / nthreads;
    if (rank == 0) {

        num = smalloc(sizeof(int)*range);
        den = smalloc(sizeof(int)*range);
        totalnum = smalloc(sizeof(int)*range);
        totalden = smalloc(sizeof(int)*range);

    } else {

        num = smalloc(sizeof(int) * slice);
        den = smalloc(sizeof(int) * slice);
        totalnum = smalloc(sizeof(int)*range);
        totalden = smalloc(sizeof(int)*range);
    }

    j = 0;
    omp_set_dynamic(0);    
    omp_set_num_threads(4);
    #pragma omp parallel for private(i, j, n) default(shared) 
    for (i = start + rank * slice; i < start + (rank + 1) * slice; i++) { 
            j = i - (start + rank * slice);
            num[j] = sumdiv(i);
            den[j] = i;

            n = gcd(num[j], den[j]);
            num[j] /= n;
            den[j] /= n;
    }
    if (rank != 0) {
        MPI_Send(num, slice, MPI_INT, 0, 0, MPI_COMM_WORLD);
        MPI_Send(den, slice, MPI_INT, 0, 1, MPI_COMM_WORLD);
    } else {
        for (i = 1; i < nthreads; i++)  {
            MPI_Recv(num + (i * (slice)), slice, MPI_INT, i, 0, MPI_COMM_WORLD, 0);
            MPI_Recv(den + (i * (slice)), slice, MPI_INT, i, 1, MPI_COMM_WORLD, 0);
        }
    }

    if (rank == 0) {
        for (i = 1; i < nthreads; i++) {
            MPI_Send(num, range, MPI_INT, i, 2, MPI_COMM_WORLD);
            MPI_Send(den, range, MPI_INT, i, 3, MPI_COMM_WORLD);
        }
    } else {
        MPI_Recv(totalnum, range, MPI_INT, 0, 2, MPI_COMM_WORLD,0);
        MPI_Recv(totalden, range, MPI_INT, 0, 3, MPI_COMM_WORLD,0);
    }

    /* Check friendly numbers. */
    nfriends = 0;
    if (rank == 0) {
        omp_set_dynamic(0);
        omp_set_num_threads(4);
        #pragma omp parallel for private(i, j) default(shared) reduction(+:nfriends)
        for (i = rank; i < range; i += nthreads) {
            for (j = 0; j < i; j++) {
                /* Friends. */
                if ((num[i] == num[j]) && (den[i] == den[j])) 
                    nfriends++;
            }
        }
    } else {
        omp_set_dynamic(0);
        omp_set_num_threads(4);
        #pragma omp parallel for private(i, j) default(shared) reduction(+:nfriends)
        for (i = rank; i < range; i += nthreads) {
            for (j = 0; j < i; j++) {
                /* Friends. */
                if ((totalnum[i] == totalnum[j]) && (totalden[i] == totalden[j])) 
                    nfriends++;
            }
        }

    }
    if (rank == 0) {
        for (i = 1; i < nthreads; i++) {
            MPI_Recv(&rcv_friends, 1, MPI_INT, i, 4, MPI_COMM_WORLD, 0);
            nfriends += rcv_friends;
        }
    } else {
        MPI_Send(&nfriends, 1, MPI_INT, 0, 4, MPI_COMM_WORLD);
    }

    free(num);
    free(den);

    return (nfriends);
}

Во время выполнения я наблюдал следующее поведение:

Когда я запускаю mpirun с 4 и 8 хостами, каждый из хостов использует 4 потока для обработки,как и ожидалось.

Однако при работе с использованием только 2 хостов на каждом компьютере используется только 1 поток.Что может вызвать такое поведение?Есть ли альтернатива «принудительному» использованию 4 потоков в случае 2 хостов?

1 Ответ

0 голосов
/ 24 июня 2018

Я предполагаю, что вы используете Open MPI.

Политика связывания по умолчанию - привязка к домену сокета или numa (в зависимости от вашей версии).Я предполагаю, что ваши узлы являются одним сокетом, что означает, что одна задача MPI привязана к 4 ядрам, и тогда среда выполнения OpenMP, скорее всего, запустит 4 потока OpenMP.

Особый случай - когда вы запускаете только 2 задачи MPI.В этом случае политика привязки заключается в привязке к ядру, что означает, что одна задача MPI связана только с одним ядром, и, следовательно, среда выполнения OpenMP запускает только один поток OpenMP.

Для достижения желаемого поведения,вы можете

mpirun --bind-to numa -np 2 ...

Если это не удастся, вы можете вернуться к

mpirun —-bind-to socket -np 2 ...
...