MPI + OpenMP

  • Гибридное программирование MPI + OpenMP использует MPI для распараллеливания между SMP узлами и OpenMP для распараллеливания между ядрами каждого из этих узлов.
  • При запуске задачи с использованием планировщика PBS надо учитывать, что значение переменной окружения '$OMP_NUM_THREADS' определенно только на одном узле ('Mother Superior'), т.е. остальные узлы по умолчанию не знают, сколько OpenMP потоков было запрошено.


  • Пример программы 'hello_mpi_mp.c' на языке C:
    #include <mpi.h>
    #include <omp.h>
    #include <stdio.h>
    #include <unistd.h>
    
    int main(int argc, char** argv)
    {
        int size, rank;
        int nthreads, tid;
        MPI_Status status;
        char host[32];
        int i;
    
        MPI_Init(&argc,&argv);
        MPI_Comm_size(MPI_COMM_WORLD,&size);
        MPI_Comm_rank(MPI_COMM_WORLD,&rank);
    
    // Environment variable OMP_NUM_THREADS is defined only on the 'Mother Superior' node.
    // So we need to send it value to other nodes and apply there.
        if ( rank == 0 ) {
            #pragma omp parallel
            {
                nthreads = omp_get_num_threads();
            }
            for ( i=1; i<size; i++ ) {
                MPI_Send(&nthreads,1,MPI_INT,i,0,MPI_COMM_WORLD);
            }
        } else {
            MPI_Recv(&nthreads,1,MPI_INT,MPI_ANY_SOURCE,0,MPI_COMM_WORLD, &status);
            omp_set_num_threads(nthreads);
        }
    
        gethostname(host,32);
        #pragma omp parallel private(tid,nthreads)
        {
            nthreads = omp_get_num_threads();
            tid = omp_get_thread_num();
            printf("MPI rank %d from %d, host %s. Thread %d from %d.\n",rank,size,host,tid,nthreads);
        }
        MPI_Finalize();
        return 0;
    }


  • Выберем реализацию MPI:
    mpi-selector --set openmpi_gcc-1.4.3
  • Перезапустим SSH сессию.
  • Откомпилируем в исполняемый файл hello_mpi_mp:
    mpicc -fopenmp -o hello_mpi_mp hello_mpi_mp.c

    Также для компиляции можно использовать makefile следующего вида:

    hello_mpi_mp : hello_mpi_mp.c
         mpicc -fopenmp -o $@ hello_mpi_mp.c


  • Для постановки задачи в очередь создадим скрипт submit.sh следующего вида:
    #!/bin/sh
    
    #PBS -l walltime=00:00:20
    #PBS -l select=3:ncpus=4:mpiprocs=1:ompthreads=4:mem=2000m
    
    cd $PBS_O_WORKDIR
    mpirun -hostfile $PBS_NODEFILE ./hello_mpi_mp

    В данном примере запрашивается 3 раза по 4 ядра и будет запущено 3 MPI-процесса, порождающих по 4 OpenMP потока (по одному потоку на ядро). Ограничение на оперативную память (2000MB) применяется к каждому MPI процессу, а не к отдельному OpenMP потоку.

  • Поставим задачу в очередь:
    qsub submit.sh
  • После завершения задачи получим примерно следующее:
    MPI rank 1 from 3, host cn03. Thread 3 from 4.
    MPI rank 1 from 3, host cn03. Thread 2 from 4.
    MPI rank 1 from 3, host cn03. Thread 1 from 4.
    MPI rank 1 from 3, host cn03. Thread 0 from 4.
    MPI rank 0 from 3, host cn01. Thread 2 from 4.
    MPI rank 0 from 3, host cn01. Thread 3 from 4.
    MPI rank 0 from 3, host cn01. Thread 1 from 4.
    MPI rank 0 from 3, host cn01. Thread 0 from 4.
    MPI rank 2 from 3, host cn05. Thread 0 from 4.
    MPI rank 2 from 3, host cn05. Thread 1 from 4.
    MPI rank 2 from 3, host cn05. Thread 2 from 4.
    MPI rank 2 from 3, host cn05. Thread 3 from 4.