問題
OpenMPを使って並列化されている処理をpthreadで作ったthreadから呼ぶことを考える。例えば、以下のようなコード。
main関数の中でpthreadを4つ作り、それぞれのthreadでループをOpenMPで並列化した処理を実行したい。
test_thread_omp.cpp
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <omp.h>
#define NTHREADS 4
void *myFun(void *x)
{
int tid;
tid = *((int *) x);
printf("Hi from thread %d!\n", tid);
#pragma omp parallel
{
int n = omp_get_num_threads();
#pragma omp master
printf("num threads: %d\n", n);
#pragma omp for
for( int i=0; i < n; i++) {
int t = omp_get_thread_num();
printf("I'm thread %d-%d!\n", tid, t );
}
}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t threads[NTHREADS];
int thread_args[NTHREADS];
int rc, i;
/* spawn the threads */
for (i=0; i<NTHREADS; ++i)
{
thread_args[i] = i;
printf("spawning thread %d\n", i);
rc = pthread_create(&threads[i], NULL, myFun, (void *) &thread_args[i]);
}
/* wait for threads to finish */
for (i=0; i<NTHREADS; ++i) {
rc = pthread_join(threads[i], NULL);
}
return 0;
}
このようなpthreadとOpenMPの混在はWindowsやLinuxでは問題なく動くようであるが、Macの場合にはmain threadから呼ばないとSEGVが発生するという情報を発見した(http://stackoverflow.com/a/8446942)。
これを実際に自分で検証した。
OSXのデフォルトのコンパイラclangではそもそもOpenMPに非対応のためhomebrewでgccを入れる。
gccを入れる方法は Homebrew で gcc4.7 を入れる を参照していただきたい。
実験結果
gcc-4.9で実験した場合、必ずセグメンテーションフォールトが発生する。
spawning thread 0
spawning thread 1
Hi from thread 0!
spawning thread 2
Hi from thread 1!
spawning thread 3
zsh: segmentation fault ./a.out
デバッガでバックトレースをとると
$ lldb ./a.out
(lldb) target create "./a.out"
Current executable set to './a.out' (x86_64).
(lldb) run
Process 99724 launched: './a.out' (x86_64)
spawning thread 0
spawning thread 1
Hi from thread 0!
spawning thread 2
Process 99724 stopped
* thread #2: tid = 0x24c7ada, 0x00000001001c164b libgomp.1.dylib`gomp_resolve_num_threads + 43, stop reason = EXC_BAD_ACCESS (code=1, address=0x50)
frame #0: 0x00000001001c164b libgomp.1.dylib`gomp_resolve_num_threads + 43
libgomp.1.dylib`gomp_resolve_num_threads:
-> 0x1001c164b <+43>: movq 0x50(%rax), %rdx
0x1001c164f <+47>: leaq 0xebca(%rip), %rax ; gomp_global_icv
0x1001c1656 <+54>: leaq 0x70(%rdx), %rbx
0x1001c165a <+58>: testq %rdx, %rdx
というエラーが起きて、どうやらOpenMPがスレッド数を取得しようとしたところで変なアドレスにアクセスしている模様。
一方、gcc-5で実験するとちゃんと動いてくれた。
spawning thread 0
spawning thread 1
Hi from thread 0!
spawning thread 2
Hi from thread 1!
spawning thread 3
Hi from thread 2!
Hi from thread 3!
I'm thread 2-2!
I'm thread 2-1!
num threads: 4
I'm thread 2-3!
num threads: 4
I'm thread 0-1!
I'm thread 0-2!
I'm thread 0-3!
I'm thread 2-0!
I'm thread 1-1!
I'm thread 1-2!
num threads: 4
I'm thread 1-3!
I'm thread 3-1!
I'm thread 3-2!
num threads: 4
I'm thread 0-0!
I'm thread 3-3!
I'm thread 1-0!
I'm thread 3-0!
結論
pthreadとOpenMPを混在させたい時はhomebrewでgcc5をインストールする