Home > Archives > 2008年12月03日

2008年12月03日

MPICH1とMPICH2とスレッド

とりあえず,スレッドサポートはMPI-2で初期化関数がMPI_Init_threadとなる.んで,こいつの第三引数に使いたいスレッドサポートレベルを投げる.その値は,MPI_THREAD_SINGLE(スレッドなし),MPI_THREAD_FUNNELED(メインのスレッドだけMPI使う),MPI_THREAD_SERIALIZED(同時には一つのスレッドだけがMPIを使用),MPI_THREAD_MULTIPLE(どうとでも使え)のいずれか.ということで,MPI_THREAD_MULTIPLEを投げておけば安全.でも実際にはサポートされていない場合もあるのが残念.

MPICHの1.2.7p1だと手元でコンパイルした限りではMPI_THREAD_FUNNELEDしかサポートしてないらしい(ch_p4, ch_shmem).よって,メインスレッド以外でMPI通信しつつ,メインスレッドが別スレッドを寝て待つとかすると挙動不審になる.ときどきメインスレッドが起きるせいか,待っていると取り合えず動いてくれるように見えるときもある.ま,基本MPICH1でスレッド使うなと.

MPICH2のほうだと,ch3:shmとch3:ssmがMPI_THREAD_SERIALIZEDを,ch3:sockとch3:nemesisがMPI_THREAD_MULTIPLEをサポートしているらしい(少なくともconfigureで指定できる).ch3:sockに関してはそれなりにスレッドでMPIをぐちゃぐちゃに使う下のプログラムでもちゃんと動いた.ch3:shmだと別スレッドがrecvで待っているときにbarrierとか叫んだときに動かなくなったりする.

ということで,MPICH2使えと.

#include <mpi.h>
#include <iostream>
#include <iomanip>
#include <unistd.h>
#include <pthread.h>
 
enum {
  TAG_1,
  TAG_4,
};
 
pthread_t thread;
int rank;
int procs;
 
void* thread_func(void* arg)
{
  std::cout << "[" << rank << ",t] entering thread_func" << std::endl;
  int d = 0;
  int target = (rank+procs-1) % procs;
  MPI_Status st;
  std::cout << "[" << rank << ",t] receiving form " << target << std::endl;
  MPI_Recv(&d, sizeof(int), MPI_BYTE, target, TAG_1, MPI_COMM_WORLD, &st);
  std::cout << "[" << rank << ",t] received form " << target << std::endl;
  d = d + 1;
  std::cout << "[" << rank << ",t] BARRIOR!!! " << std::endl;
  MPI_Barrier(MPI_COMM_WORLD);
  std::cout << "[" << rank << ",t] sending to " << target << std::endl;
  MPI_Send(&d, sizeof(int), MPI_BYTE, target, TAG_4, MPI_COMM_WORLD);
  std::cout << "[" << rank << ",t] sent to " << target << std::endl;
 
  std::cout << "[" << rank << ",t] exitting thread_func" << std::endl;
  return NULL;
}
 
void* main_func(void* arg)
{
  std::cout << "[" << rank << ",m] entering main_func" << std::endl;
  int d = rank;
  int target = (rank+1) % procs;
  MPI_Status st;
  std::cout << "[" << rank << ",m] sending to " << target << std::endl;
  MPI_Send(&d, sizeof(int), MPI_BYTE, target, TAG_1, MPI_COMM_WORLD);
  std::cout << "[" << rank << ",m] sent to " << target << std::endl;
  std::cout << "[" << rank << ",m] receiving form " << target << std::endl;
  MPI_Recv(&d, sizeof(int), MPI_BYTE, target, TAG_4, MPI_COMM_WORLD, &st);
  std::cout << "[" << rank << ",m] received form " << target << std::endl;
  void *ret;
  std::cout << "[" << rank << ",m] joining" << std::endl;
  pthread_join(thread, &ret);
  std::cout << "[" << rank << ",m] joined" << std::endl;
  std::cout << "[" << rank << ",m] exiting main_func" << std::endl;
  return NULL;
}
 
const char *thread_support_str(int p)
{
  switch(p){
  case MPI_THREAD_SINGLE:     return "MPI_THREAD_SINGLE";
  case MPI_THREAD_FUNNELED:   return "MPI_THREAD_FUNNELED"; 
  case MPI_THREAD_SERIALIZED: return "MPI_THREAD_SERIALIZED";
  case MPI_THREAD_MULTIPLE:   return "MPI_THREAD_MULTIPLE";
  default: return "What?";
  }
}
 
int main(int argc, char *argv[])
{
  int prob;
  MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &prob);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);
  MPI_Comm_size(MPI_COMM_WORLD, &procs);
  if(rank==0) {
    std::cout << "# " << procs << " procs" << std::endl;
    std::cout << "# thread support = " << thread_support_str(prob)  << std::endl;
  }
  pthread_create(&thread, NULL, thread_func, NULL);
  main_func(NULL);
  MPI_Finalize();
  return 0;
}

Home > Archives > 2008年12月03日

Search
Feeds

Page Top