4月
02
AsiaBSDCon 帰りの同僚に先週聞いたんですが、OpenBSD 5.0 の pthread 実装では、複数スレッド走らせても 1 コアしか使わない(ので速くならない)そうです。
えー、と思って以下のプログラムで 2 スレッド走らせてみます。
#include <pthread.h> volatile int n; void *proc(void *arg) { for (;;) n++; } int main(void) { pthread_t ptid; pthread_create(&ptid, NULL, proc, NULL); proc(NULL); return 0; }
んで top コマンドで CPU 使用率を見てみると…
load averages: 0.38, 0.15, 0.10 devopenbsd.local 10:04:05 26 processes: 24 idle, 2 on processor CPU0 states: 0.0% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle CPU1 states: 0.0% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle CPU2 states: 0.0% user, 0.0% nice, 0.0% system, 0.0% interrupt, 100% idle CPU3 states: 100% user, 0.0% nice, 0.0% system, 0.0% interrupt, 0.0% idle Memory: Real: 14M/137M act/tot Free: 347M Cache: 88M Swap: 0K/1024M PID USERNAME PRI NICE SIZE RES STATE WAIT TIME CPU COMMAND 28750 umezawa 64 0 1656K 1244K onproc/1 - 0:17 56.05% pthread-run 1304 _syslogd 2 0 624K 960K sleep/0 poll 2:23 0.00% syslogd 14212 _pflogd 4 0 644K 384K sleep/2 bpf 1:32 0.00% pflogd 7883 umezawa 2 0 3476K 2372K sleep/1 select 1:12 0.00% sshd (以下略)
ほんまや。
ちなみに Linux (CentOS 6, kernel-2.6.32-220.7.1.el6.x86_64) だと、以下のようにちゃんと 2 コア使っています。
top - 10:06:00 up 18 days, 8:43, 9 users, load average: 1.06, 0.28, 0.09 Tasks: 158 total, 2 running, 156 sleeping, 0 stopped, 0 zombie Cpu0 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu1 :100.0%us, 0.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu2 : 0.3%us, 0.3%sy, 0.0%ni, 99.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu3 :100.0%us, 0.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 502224k total, 438068k used, 64156k free, 33720k buffers Swap: 1015800k total, 21324k used, 994476k free, 195152k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 13661 umezawa 20 0 16440 496 392 R 200.0 0.1 1:24.29 pthread-run 1 root 20 0 19272 696 508 S 0.0 0.1 0:01.21 init 2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd 3 root RT 0 0 0 0 S 0.0 0.0 0:00.13 migration/0 (以下略)
OpenBSD の pthread にはスピンロックがないんですが、それはこのせいだったんですね。(シングルプロセッサ環境ではスピンロックは使っていけない)
ただし、よくあるユーザーランドスレッド(協調的マルチスレッド)と違い、ちゃんとプリエンプティブです。つまり、スレッドを切り替える関数やシステムコールを呼ばなくても、カーネルが勝手に切り替えてくれます。これは以下のコードで確認できます。
#include <pthread.h> #include <stdio.h> #include <unistd.h> volatile long long n0 = 0; volatile long long n1 = 0; void *proc(void *arg) { volatile long long *np = arg; for (;;) (*np)++; } void sigint(int sig) { printf("\n"); printf("n0 = %lld\n", n0); printf("n1 = %lld\n", n1); } int main(void) { pthread_t ptid; signal(SIGINT, sigint); pthread_create(&ptid, NULL, proc, (void *)&n1); proc((void *)&n0); return 0; }
printf() はシグナルセーフではないとか細かいことは言いっこなしで。
で、これを実行すると、
[umezawa@devopenbsd:ttyp0 ~]$ ./pthread-run ^C n0 = 203792139 n1 = 206249524 ^C n0 = 470482870 n1 = 474856626 ^C n0 = 830385269 n1 = 828581258 ^C n0 = 1135243828 n1 = 1139698685
となって、2 つのスレッドが同じ時間だけ走っていることが分かります。(協調的マルチスレッドの場合だと一方のカウンタだけが進んでいくはず)
OpenBSD-current では複数コア使うようにコーディング中だそうです。
5.1-current で試してみたら、2CPU で動くようにようになってました。
http://yasuoka.net/~yasuoka/hack-2012.html#d20120502