これはNetBSD Advent Calendar 2014の21日めの記事です。
最近のパソコンはもうあたりまえにマルチコアなCPUが使われています。 2つとか4つとかそれ以上とか。 それどころか携帯電話というかスマートフォンでさえマルチコアが当たり前です。 一方で「マルチコアを効率良く使うにはアプリケーションがマルチスレッドで動作する必要がある」なんて話もよく聞きます。
気づけば私が普段使っているNetBSD環境もマルチコアのCPUが使われています。 そんなわけでこの環境でマルチスレッド動作しマルチコアで動くプログラムを作って確認してみようと思った話です。
バリバリにプログラム書いてる人には退屈で当たり前な話ですがご容赦くださいまし。
NetBSDはいわゆるUNIX系のOSです。この世界ではPOSIX Thread libray - pthreadという標準仕様があります。 pthreadはアプリケーションを作る時の(主にC言語用)プログラムライブラリの仕様です。もちろんNetBSDでもこの仕様に対応しています。
NetBSDのman pageでpthread(3)という項目や、市販の書籍を見たりするとこのライブラリを使うとマルチスレッドで動作するアプリケーションが作れるみたいです。
私の手元にはO'REILLYのPthreadsプログラミングという書籍があります(この本は1998年と15年以上も前に出たものだったりします。最近はもっといい本があるかもしれません)。この本をチラ見しつつ、man pageも読みつつ実際にマルチスレッドを動かしてみます。
テストに使う環境は以下の環境です。
無駄にメモリ多い感じですがそれはそれとして、低価格だけど4コアなやつです。 NetBSD起動時には以下の様に表示され、4コアあることがわかります。
cpu0 at mainbus0 apid 0: AMD Athlon(tm) 5350 APU with Radeon(tm) R3 , id 0x700f01 cpu1 at mainbus0 apid 1: AMD Athlon(tm) 5350 APU with Radeon(tm) R3 , id 0x700f01 cpu2 at mainbus0 apid 2: AMD Athlon(tm) 5350 APU with Radeon(tm) R3 , id 0x700f01 cpu3 at mainbus0 apid 3: AMD Athlon(tm) 5350 APU with Radeon(tm) R3 , id 0x700f01
とりあえず先のPthreads本を見ながらこれ以上無いすげぇ簡単なプログラムを作ってみます。
#include <stdio.h> #include <stdlib.h> #include <err.h> #include <pthread.h> #define NUMTHREAD 4 unsigned int count[NUMTHREAD]; void *thread_proc(void *p) { int num = *(int *)p; while(1) { if (count[num]==0xffffffff) printf("%d thread:overflow\n",num); count[num]++ ; } } int main() { int ret; int i; int tnum[NUMTHREAD]; pthread_t pt[NUMTHREAD]; for (i=0;i<NUMTHREAD;i++) { tnum[i]=i; count[i]=0; ret = pthread_create(&pt[i], NULL, thread_proc, &tnum[i]); if (ret !=0) err(1, "pthread_create"); } for (i=0;i< NUMTHREAD;i++ ) pthread_join(pt[i],NULL); }
こんな処理普通はあるわけないのですが、thread_proc()
という関数で単に無限ループでカウントアップするルーチンがあり、これをmain関数内でNUMTHREAD
(=4)回ループしてスレッドを4個生成して動かしています。
真っ当な処理ではスレッド間でデータのやりとりや、そのための同期処理とかも必要になるはずなのですがここではとにかくスレッドを動かすだけにしてます。
ではこれをコンパイルしてみましょう。普通にcc
でコンパイルします。
% cc -o threadtest trehadtest.c /var/tmp//ccoB22ww.o: In function `main': trehadtest.c:(.text+0xb7): undefined reference to `pthread_create' %
リンク時にpthread_create
が見つからないというエラーが出てしまいました。
実はpthreadを使用するプログラムをコンパイルする場合はコンパイルオプションに-pthread
を付けなければならないようです。
% cc -o threadtest -pthread trehadtest.c %
今度は正しくできたようなので実行してみます。
% ./threadtest
このプログラムは単純に無限ループで終了条件がありませんので、終わる場合はキーボードからControl-Cを入力する必要があります。
プログラム実行中の様子を他のセッションから確認してみます。別のセッションからtop(1)コマンドを実行します。
% top -1 -t load averages: 3.99, 3.46, 2.18; up 0+16:21:57 01:47:47 86 threads: 20 idle, 1 runnable, 60 sleeping, 1 zombie, 4 on CPU CPU0 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU1 states: 92.1% user, 0.0% nice, 0.0% system, 0.0% interrupt, 7.9% idle CPU2 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU3 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle Memory: 4496M Act, 164K Inact, 2188K Wired, 8948K Exec, 4475M File, 11G Free Swap: 16G Total, 16G Free PID LID USERNAME PRI STATE TIME WCPU CPU NAME COMMAND 3885 5 oshima 27 CPU/3 8:38 93.55% 93.55% - threadtest 3885 4 oshima 27 CPU/2 8:41 92.63% 92.63% - threadtest 3885 3 oshima 27 CPU/0 8:36 81.88% 81.88% - threadtest 3885 2 oshima 27 RUN/0 8:31 79.98% 79.98% - threadtest (略)
threadtestのプロセスはLIDが1から5まであり、そのうち2から5の4つがほぼ全てのCPUを使ってます。 これは作成した無限ループするスレッドと一致します。 タイミングによってCPUも時々4つとも100%になったり、ガクッと一つが減ったりもするようですが、ちゃんと複数のCPUにわかれて動いているようです。
4つのCPUで4つのスレッドが動いているので都合がいいのですが、ではもっとたくさんにするとどうなるか試してみます。
先のプログラムのNUMTHREAD
を4から10にしてみます。
load averages: 5.64, 4.23, 3.04; up 0+16:29:36 01:55:26 92 threads: 20 idle, 7 runnable, 60 sleeping, 1 zombie, 4 on CPU CPU0 states: 99.8% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.2% idle CPU1 states: 99.8% user, 0.0% nice, 0.2% system, 0.0% interrupt, 0.0% idle CPU2 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU3 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle Memory: 4496M Act, 164K Inact, 2188K Wired, 8948K Exec, 4475M File, 11G Free Swap: 16G Total, 16G Free PID LID USERNAME PRI STATE TIME WCPU CPU NAME COMMAND 21950 9 oshima 26 RUN/2 0:12 48.90% 35.60% - threadtest 21950 4 oshima 26 CPU/2 0:11 44.14% 32.13% - threadtest 21950 6 oshima 25 RUN/1 0:10 42.06% 30.62% - threadtest 21950 11 oshima 26 RUN/0 0:11 40.79% 29.69% - threadtest 21950 8 oshima 27 RUN/3 0:09 39.71% 28.91% - threadtest 21950 5 oshima 27 RUN/3 0:10 37.57% 27.34% - threadtest 21950 10 oshima 25 CPU/3 0:09 35.22% 25.63% - threadtest 21950 3 oshima 27 RUN/0 0:09 34.68% 25.24% - threadtest 21950 2 oshima 26 CPU/0 0:08 30.79% 22.41% - threadtest 21950 7 oshima 27 RUN/3 0:08 29.72% 21.63% - threadtest (略)
LIDが11まで増えました。CPUコア数以上のスレッドを作るとちょっと使用量はバラつきがあるみたいですが、とりあえずはすべてのスレッドが平行して動いているようです。また、CPU毎の使用率は常にほぼ100%となり、マルチコアがちゃんと使われているようです。
さて、せっかくのNetBSDで動かすのですから、同じプログラムを別のCPUを使ったマシンで動かしてみましょう。
残念ながらamd64/i386以外にマルチプロセッサな環境を持ち合わせていませんので、試したのは以下のものです。
もちろん1コアの環境です。しかも超マイナーなNetBSD/sh3アーキテクチャです。さらに事情により1年前のNetBSD-currentとOSが微妙な感じですが、とりあえず気にしないことにしておきます。
さっさとコンパイルして実行してみました。topの出力は以下の様になりました。
load averages: 7.96, 2.78, 1.12; up 0+16:27:50 02:31:03 57 threads: 5 idle, 10 runnable, 41 sleeping, 1 on CPU CPU states: 99.7% user, 0.0% nice, 0.3% system, 0.0% interrupt, 0.0% idle Memory: 22M Act, 11M Inact, 5148K Wired, 5956K Exec, 21M File, 1456K Free Swap: 1007M Total, 1007M Free PID LID USERNAME PRI STATE TIME WCPU CPU NAME COMMAND 1983 8 oshima 32 RUN 0:09 9.95% 9.86% - threadtest 1983 9 oshima 30 RUN 0:08 9.85% 9.77% - threadtest 1983 10 oshima 32 RUN 0:09 9.65% 9.57% - threadtest 1983 2 oshima 32 RUN 0:09 9.51% 9.42% - threadtest 1983 5 oshima 32 RUN 0:09 9.36% 9.28% - threadtest 1983 3 oshima 32 RUN 0:09 9.26% 9.18% - threadtest 1983 6 oshima 32 RUN 0:09 9.21% 9.13% - threadtest 1983 11 oshima 31 RUN 0:09 8.77% 8.69% - threadtest 1983 7 oshima 32 RUN 0:09 8.57% 8.50% - threadtest 1983 4 oshima 31 RUN 0:09 8.52% 8.45% - threadtest (略)
さすがに1コアしかないので、10スレッド動かすとそれぞれのスレッドのCPU使用率は10%未満になりますが、それでもちゃんと動いているようです。
さらに別のアーキテクチャ(m68kのNetBSD/x68kとか)でも試してみましたが、ほとんど同じなので出力結果は割愛します(全体として実効速度がとても遅いだけ)。
NetBSDでpthreadを使ったプログラムは、マルチコアな環境で動かすとちゃんとマルチコアで実行されるみたいです。 なので、今時のマルチコアマシンでは有効に機能しそうです。 また、シングルコアな環境でも動きますがその場合は1つのCPUコアを適当に時分割して平行して動いてくれるようです。
またamd64といった通常よく使う一般的なアーキテクチャだけじゃなく、謎マシンと呼ばれるようなマイナーアーキテクチャマシンでもそのまま動くことがわかりました。
CPUコア以上のスレッドが全力で動いている場合にも適当に実行を切り替えてくれるみたいですが、なんとなく実行頻度にバラつきがあるような気がします。このあたりどうやって切り替わっているのか気になります。切り替えの頻度とか、そのオーバーヘッドとか確認してみたかったりします。
また、実際のプログラムでは複数のスレッド間で同期処理やデータの共有/やりとりをする必要があります。そのためのpthread_mutex(4)とかpthread_cond(4)とかさらにはmutex_spin(4)やらsem_wait(4)やらを色々使うことになるのですか、こういうの使った場合にもどんな動きになるのか確認してみたいところです。どういう確認方法がいいのかよくわかってませんが。
マルチスレッドの効能はマルチコアだけじゃなくDISK I/OやNetwork I/O のように「CPUの実行速度に比較して遅い、または待ちが必要」な処理と他の処理を同時に動かす必要がある場合にも有効と言われています。このあたりもわかりやすく確認してみたいです。これで効率よく実行できればシングルコアな謎マシンでも恩恵を受けられるのでちょっと期待したいところです。
コンパイル時に-pthread
というオプションを付けていましたが、実はmanpageには、-lpthread
と書いてあったりします。これはライブラリであるlibpthread.soをリンクするという意味になります。-pthread
オプションをつけた場合には内部的に-lpthread
も自動的に付けてくれているのだと思います。
が、この2つ、1文字多いだけですが他に違いはないのでしょうか。
ということで両者のコンパイル時に-v
を付けて確認してみます。
まず-pthread
の場合です。
Using built-in specs.
COLLECT_GCC=cc
Target: x86_64--netbsd
Configured with:
(略)
Thread model: posix
gcc version 4.8.3 (nb1 20140527)
COLLECT_GCC_OPTIONS='-v' '-o' 'threadtest' '-pthread' '-mtune=nocona' '-march=x86-64'
/usr/libexec/cc1 -quiet -v -D_REENTRANT -D_PTHREADS
threadtest.c -quiet -dumpbase threadtest.c -mtune=nocona -march=x86-64 -auxbase threadtest -version -o /var/tmp//cc9Yv16W.s
GNU C (nb1 20140527) version 4.8.3 (x86_64--netbsd)
compiled by GNU C version 4.8.4 20141009 (prerelease), GMP version 5.1.3, MPFR version 3.1.2, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
#include "..." search starts here:
#include <...> search starts here:
/usr/include/gcc-4.8
/usr/include
End of search list.
GNU C (nb1 20140527) version 4.8.3 (x86_64--netbsd)
compiled by GNU C version 4.8.4 20141009 (prerelease), GMP version 5.1.3, MPFR version 3.1.2, MPC version 1.0.1
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
Compiler executable checksum: 6c545c72a86eb617b8e6548b191c9444
COLLECT_GCC_OPTIONS='-v' '-o' 'threadtest' '-pthread' '-mtune=nocona' '-march=x86-64'
as -v -o /var/tmp//ccEE9PT7.o /var/tmp//cc9Yv16W.s
GNU assembler version 2.23.2 (x86_64--netbsd) using BFD version (NetBSD Binutils nb1) 2.23.2
COMPILER_PATH=/usr/libexec/
LIBRARY_PATH=/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-o' 'threadtest' '-pthread' '-mtune=nocona' '-march=x86-64'
ld --eh-frame-hdr -dc -dp -e _start -dynamic-linker /usr/libexec/ld.elf_so -o threadtest /usr/lib/crt0.o /usr/lib/crti.o /usr/lib/crtbegin.o /var/tmp//ccEE9PT7.o -lgcc --as-needed -lgcc_s --no-as-needed -lpthread -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o
次に-lpthread
だけの場合です。
Using built-in specs. COLLECT_GCC=cc Target: x86_64--netbsd Configured with: (略) Thread model: posix gcc version 4.8.3 (nb1 20140527) COLLECT_GCC_OPTIONS='-v' '-o' 'threadtest' '-mtune=nocona' '-march=x86-64' /usr/libexec/cc1 -quiet -v threadtest.c -quiet -dumpbase threadtest.c -mtune=nocona -march=x86-64 -auxbase threadtest -version -o /var/tmp//cczUfETT.s GNU C (nb1 20140527) version 4.8.3 (x86_64--netbsd) compiled by GNU C version 4.8.4 20141009 (prerelease), GMP version 5.1.3, MPFR version 3.1.2, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 #include "..." search starts here: #include <...> search starts here: /usr/include/gcc-4.8 /usr/include End of search list. GNU C (nb1 20140527) version 4.8.3 (x86_64--netbsd) compiled by GNU C version 4.8.4 20141009 (prerelease), GMP version 5.1.3, MPFR version 3.1.2, MPC version 1.0.1 GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: 6c545c72a86eb617b8e6548b191c9444 COLLECT_GCC_OPTIONS='-v' '-o' 'threadtest' '-mtune=nocona' '-march=x86-64' as -v -o /var/tmp//ccX49pjn.o /var/tmp//cczUfETT.s GNU assembler version 2.23.2 (x86_64--netbsd) using BFD version (NetBSD Binutils nb1) 2.23.2 COMPILER_PATH=/usr/libexec/ LIBRARY_PATH=/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-o' 'threadtest' '-mtune=nocona' '-march=x86-64' ld --eh-frame-hdr -dc -dp -e _start -dynamic-linker /usr/libexec/ld.elf_so -o threadtest /usr/lib/crt0.o /usr/lib/crti.o /usr/lib/crtbegin.o /var/tmp//ccX49pjn.o -lpthread -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o
-pthread
の場合、リンク時のldで-lpthread
が付いている以外に、コンパイル時のcc1に-D_REENTRANT -D_PTHREADS
という2つのdefineが追加されています。/usr/include/以下のヘッダをgrepで検索するとstdlo.hやstdlib.hでも参照しているところがあるようです。
このことからpthreadライブラリをリンクするのではなくコンパイル時に-pthread
オプションを付けたほうが良さげです。
もう6年近く前になりますが、2009年4月のNetBSD 5.0のリリース時、リリースアナウンスに主な変更点としてマルチプロセッサ対応の劇的な改善、というのがありました。
NetBSD 5.0 features greatly improved performance and scalability on modern multiprocessor (SMP) and multi-core systems. Multi-threaded applications can now efficiently make use of more than one CPU or core, and system performance is much better under I/O and network load, benefiting, for example, server, scientific, and software development workloads.
[以下適当俺様訳]
NetBSD 5.0は現代的なマルチプロセッサ(SMP)/マルチコアシステムにおいて劇的なパフォーマンスとスケーラビリティの改善を行いました。マルチスレッドアプリケーションは複数のCPU/コアを効率的に使用可能になり、I/Oやネットワーク負荷でのシステムパフォーマンスも向上し、例えばサーバや技術計算、ソフトウェア開発時作業に有効です。
ということでせっかくなのでNetBSD 4.xとNetBSD 5.xでテストプログラムを動かして比べてみました。実行したのは同じ4コアのAthlon 5350マシンです。
本題と関係ありませんがUSBメモリにインストールして差し替えているのでハードディスクにある環境こわさないので便利ですね
NetBSD4系はすでにサポート切れですが、その最終リリースは2008/10/14にリリースされた4.0.1らしいのでこれで動かしてみます。その結果は以下
load averages: 0.69, 0.51, 0.26 up 0 days, 10:00 08:59:01 31 processes: 29 sleeping, 2 on processor CPU0 states: 0.0% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle CPU1 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU2 states: 0.0% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle CPU3 states: 0.0% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle Memory: 16M Act, 972K Wired, 3872K Exec, 9284K File, 15G Free Swap: 129M Total, 129M Free PID USERNAME PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND 601 oshima 53 0 2128K 1132K CPU/1 0:43 99.39% 87.84% threadtest 14 root 18 0 0K 19M syncer/0 0:02 0.00% 0.00% [ioflush] (略)
CPU4個あるのに1個しか使ってくれません。また、NetBSD 4.0.1のtop(1)はスレッド表示に対応していないようなので、ps(1)の-sオプションを使ってみます。
% ps -as UID PID PPID CPU LID NLWP PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 0 598 1 0 1 1 10 0 152 2316 wait I ttyE0 0:00.01 login 100 660 598 0 1 1 18 0 232 1068 pause S ttyE0 0:00.00 -csh 100 782 660 0 1 1 28 0 100 756 - R ttyE0 0:00.00 ps -as 100 601 702 52333 1 1 53 0 2128 1132 - R ttyE1 0:53.89 ./threadt 0 632 1 0 1 1 10 0 152 2320 wait I ttyE1 0:00.00 login 100 702 632 0 1 1 18 0 232 1064 pause I ttyE1 0:00.00 -csh 0 657 1 44 1 1 3 0 60 944 ttyin I ttyE2 0:00.00 /usr/libe 0 637 1 44 1 1 3 0 60 944 ttyin I ttyE3 0:00.00 /usr/libe
なんかNLWPの数は1で、スレッド1個しかないように見えます。うーーん。テストプログラムを少し変えてみて待たせるようにしてみましたが変わりません。ちゃんとスレッド自体はバラバラに動いているようですが、外から確認する方法がよくわからない。 少なくともCPUは1コア分しか使っていません。これではかなり悲しい感じです。
次にNetBSD 5で試してみます。現在の最新はつい先日2014/11/15にリリースされたNetBSD 5.2.3です。
% top -t load averages: 9.73, 6.51, 3.21; up 0+00:10:13 09:25:11 74 threads: 20 idle, 7 runnable, 43 sleeping, 4 on CPU CPU0 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU1 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU2 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle CPU3 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle Memory: 20M Act, 4048K Inact, 1024K Wired, 5416K Exec, 13M File, 15G Free Swap: 406M Total, 406M Free PID LID USERNAME PRI STATE TIME WCPU CPU COMMAND NAME 420 4 oshima 26 RUN/2 1:15 43.61% 43.60% threadtest - 420 10 oshima 25 RUN/1 1:20 43.17% 43.16% threadtest - 420 11 oshima 25 RUN/2 1:17 40.82% 40.82% threadtest - 420 6 oshima 25 CPU/2 1:18 40.38% 40.38% threadtest - 420 2 oshima 25 RUN/1 1:09 39.94% 39.94% threadtest - 420 5 oshima 25 CPU/3 1:13 39.02% 39.01% threadtest - 420 9 oshima 25 RUN/0 1:19 38.63% 38.62% threadtest - 420 7 oshima 25 CPU/1 1:16 35.84% 35.84% threadtest - 420 8 oshima 25 RUN/2 1:15 32.38% 32.37% threadtest - 420 3 oshima 26 RUN/2 1:21 31.79% 31.79% threadtest - (略) % ps -as UID PID PPID CPU LID NLWP PRI NI VSZ RSS WCHAN STAT TTY TIME COMMAND 100 359 383 0 1 1 43 0 6556 900 - R ttyE0 0:00.00 ps -as 0 373 1 0 1 1 85 0 29272 2872 wait I ttyE0 0:00.01 login 100 383 373 0 1 1 85 0 4508 1144 pause S ttyE0 0:00.00 -csh 0 377 1 0 1 1 85 0 29272 2916 wait I ttyE1 0:00.01 login 100 404 377 0 1 1 85 0 4508 1132 pause I ttyE1 0:00.00 -csh 100 420 404 332214 11 11 28 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 10 11 28 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 9 11 26 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 8 11 26 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 7 11 26 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 6 11 28 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 5 11 27 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 4 11 28 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 3 11 28 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 2 11 28 0 29032 1100 - R- ttyE1 1:51.36 ./thre 100 420 404 332214 1 11 43 0 29032 1100 parked S ttyE1 1:51.36 ./thre 0 392 1 18 1 1 85 0 6508 1076 ttyraw I ttyE2 0:00.00 /usr/l 0 334 1 18 1 1 85 0 6508 1076 ttyraw I ttyE3 0:00.00 /usr/l
こちらではCPU4個をすべて使用し、実際に11個のスレッドを確認することができました。最初に使ったNetBSD 7.0_BETAと同じ動きです。
わざわざ古いOS使うことは普通ありえないと思いますが、NetBSD 5のリリースアナウンスにかかれているとおり、マルチプロセッサでのマルチスレッドプログラム実行パフォーマンスは劇的に上がっているといっていいみたいです(前が酷すぎただけという話もありますが)。
NetBSDというとどうしてもカーネルの中のドライバとか、謎マシンへの移植とか、そういう話が多くなりがちで、それは大変興味深いのですが、なかなか縁遠い人も多いんじゃないかと思います。でももうちょっとユーザ寄りのところ、でも既存の何かを動かすだけじゃないところ、アプリケーション作るときに関係するようなことって何か無いかな、とか思って書いてみました。
OSの重要な役割としてアプリケーションを作成・動作させるため簡便な手段を提供しハードウェアの性能/機能を自動的に引き出してくれるということもあります。 今回のネタであるpthreadを使ってちゃんとマルチコアの性能をいかせるというのは地味で当たり前のことですが、それがちゃんとできるようになっているのは嬉しいことでもあります。 この話は別にNetBSDでなくても良いし、他のOSでも同じプログラムが書けて動かせるわけですが、そういうあたりまえのプログラムも、ちゃんとNetBSD上で動かせるんだよ、ということも大事ではないかと思います。
今時はC言語レベルでpthreadを生で使うってことはあんまりないのかもしれません。より簡易な言語環境/実行環境/ライブラリなどを使うのが普通かもしれません。
NetBSD上でも、pkgsrcを使ってPerl, Ruby, Pythonなんかも普通に動きますし、使えるアーキテクチャは限られますがOpenJDKだってあるのでjavaでマルチスレッドは当たり前に動きます。一部で話題のgo言語も、同様に使えるアーキテクチャは限られますが手元のamd64環境ではマルチスレッドで動かすこともできました。この話も書こうかと思いましたがまとまってないのととっても長くなりそうなのでいずれまたの機会にでも。