K.Sasada's Home Page

Diary - 2012 May

研究日記

皐月

_15(Tue)

一眼レフカメラはでかいんだから,iPhone くっつけて Eye-Fi みたいなことをするようなものってないのかな.

ぱっとググったらこういうのがあるようだ.

最初のが(形は別にして)想像していたものだった.しかし,iPhone 無いと使えないってのは凄い.

_14(Mon)

そういえば,JavaScript ライブラリによって,最近気軽に syntax highlight ができるようになっている気がする.

機械語で,飛び先に矢印を付けてくれるようなものは気軽に出来たりしないものか.むしろ,gdb や objdump の吐く disassemble 結果が HTML で,みたいな.


GCC には,関数やグローバル変数に section attribute を付けることができる.リンカに,これはどこセクションですよ,と教えてあげる機能.

で,これを使うと局所性のあるデータを集められないかと思って,考えてみた.

==> sec.c <==
#include <stdio.h>

extern int highly_access1, highly_access2, highly_access3;

int main()
{
    printf("%p\n%p\n%p\n", &highly_access1, &highly_access2, &highly_access3);
    return 0;
}

==> sec_1.c <==
#include "sec.h"
int dummy_1_1[0x100];
int dummy_1_2[0x100];
int highly_access1 HIGHLY_ACCESS;
int dummy_1_3[0x100];
int dummy_1_4[0x100];


==> sec_2.c <==
#include "sec.h"

int dummy_2_1[0x100];
int dummy_2_2[0x100];
int highly_access2 HIGHLY_ACCESS;
int dummy_2_3[0x100];
int dummy_2_4[0x100];


==> sec_3.c <==
#include "sec.h"

int dummy_3_1[0x100];
int dummy_3_2[0x100];
int highly_access3 HIGHLY_ACCESS;
int dummy_3_3[0x100];
int dummy_3_4[0x100];


==> sec.h <==
#if 1
  #define HIGHLY_ACCESS __attribute__ ((section ("data.highly_access")))
#else
  #define HIGHLY_ACCESS /* empty */
#endif

sec.h で,section attribute を使うかどうか選択できるようにしている.sec_[123].c では,グローバル変数の束中に,それぞれ1つだけ HIGHLY_ACCESS な値があるとする.

コンパイルしてみた結果.

$ gcc -O3 sec*.c -o sec
$ objdump -t sec| sort
(略)
080495e4 g     O .data  00000000              .hidden __dso_handle
080495e8 g     O data.highly_access     00000004              highly_access1
080495e8 l    d  data.highly_access     00000000              data.highly_access
080495ec g     O data.highly_access     00000004              highly_access2
080495f0 g     O data.highly_access     00000004              highly_access3
080495f4 g       *ABS*  00000000              __bss_start
080495f4 g       *ABS*  00000000              _edata
08049600 l     O .bss   00000001              completed.5982
08049600 l    d  .bss   00000000              .bss
08049604 l     O .bss   00000004              dtor_idx.5984
08049620 g     O .bss   00000400              dummy_1_1
08049a20 g     O .bss   00000400              dummy_1_2
08049e20 g     O .bss   00000400              dummy_1_3
0804a220 g     O .bss   00000400              dummy_1_4
0804a620 g     O .bss   00000400              dummy_2_1
0804aa20 g     O .bss   00000400              dummy_2_4
0804ae20 g     O .bss   00000400              dummy_2_3
0804b220 g     O .bss   00000400              dummy_2_2
0804b620 g     O .bss   00000400              dummy_3_4
0804ba20 g     O .bss   00000400              dummy_3_1
0804be20 g     O .bss   00000400              dummy_3_2
0804c220 g     O .bss   00000400              dummy_3_3
0804c620 g       *ABS*  00000000              _end

ちゃんと,集まっていることがわかる.

これを,無効にしてみる.

08049600 l    d  .bss   00000000              .bss
08049604 l     O .bss   00000004              dtor_idx.5984
08049620 g     O .bss   00000004              highly_access1
08049640 g     O .bss   00000400              dummy_1_1
08049a40 g     O .bss   00000400              dummy_1_2
08049e40 g     O .bss   00000400              dummy_1_3
0804a240 g     O .bss   00000400              dummy_1_4
0804a640 g     O .bss   00000400              dummy_2_1
0804aa40 g     O .bss   00000004              highly_access2
0804aa60 g     O .bss   00000400              dummy_2_4
0804ae60 g     O .bss   00000400              dummy_2_3
0804b260 g     O .bss   00000400              dummy_2_2
0804b660 g     O .bss   00000400              dummy_3_4
0804ba60 g     O .bss   00000400              dummy_3_1
0804be60 g     O .bss   00000400              dummy_3_2
0804c260 g     O .bss   00000004              highly_access3
0804c280 g     O .bss   00000400              dummy_3_3
0804c680 g       *ABS*  00000000              _end

ぐちゃっとしていることがわかる.そもそも BSS だった.これはフェアじゃないかなぁ.

小崎さんによると,Linux でも read mostly,つまり殆ど変わらないで read が頻発するところは,このテクニックを使っているらしい.逆に,read mostly なデータと update が頻発するデータを混ぜると,大変なことになるらしい.ご利用は計画的に.

_13(Sun)

MacOSX の dtrace について調査したのでメモ.ユーザ定義プロバイダの話.

$ cat foo.c
#include "foo_probe.h"
#include <stdio.h>


int main(int argc, char *argv[])
{
  if (FOO_FOO_START_ENABLED()) FOO_FOO_START();
  if (FOO_FOO_MIDDLE_ENABLED()) FOO_FOO_MIDDLE("foo!");
  if (FOO_FOO_END_ENABLED()) FOO_FOO_END();
  return 0;
}

こんなコードに対して,

$ cat foo_probe.d
provider Foo {
  probe foo_start();
  probe foo_middle(char *);
  probe foo_end();
};

こんな,プロバイダ定義を書く.こいつから,.h を生成する.

$ dtrace -h -s foo_probe.d
  • -h がヘッダ生成モード
  • -s が,ターゲットとなるファイルの指定.

foo_probe.d を指定したので,foo_probe.h が出来る.-o オプションで指定可能.

foo_probe.h に,ごにょごにょ書いてあるわけです.

#define FOO_FOO_START() \
do { \
        __asm__ volatile(".reference " FOO_TYPEDEFS); \
        __dtrace_probe$foo$foo_start$v1(); \
        __asm__ volatile(".reference " FOO_STABILITY); \
} while (0)
#define FOO_FOO_START_ENABLED() \
        ({ int _r = __dtrace_isenabled$foo$foo_start$v1(); \
                __asm__ volatile(""); \
                _r; })

正直何が書いてあるかよくわからない..reference って何.

これで,そのまま gcc でビルド可能.

$ gcc -O3 foo.c

Sun のマニュアルとか,SystemTap (Linux) のマニュアルを見ると,dtrace -G foo.o とやらないといけない(dtrace 用に前処理が必要),とあるんだけど,多分 Apple は gcc を弄って,この手間を省く(むしろ,出来なくしている)ように見える.これが正しいかどうかはよくわからないけど,まぁ便利ならば入れちゃおうぜ,って精神が見える気がする.見当外れだったらごめん.

で,実際に動かしてみる.

$ ./a.out

何事も無く動く.

dtrace で実行してみる.

$ sudo dtrace -c ./a.out -P 'foo$target'
dtrace: description 'foo$target' matched 3 probes
dtrace: pid 22611 has exited
CPU     ID                    FUNCTION:NAME
  1 144192                   main:foo_start
  1 144191                  main:foo_middle
  1 144190                     main:foo_end

最初,-P foo で動かなくて大変だった.なんで $target なんて付ける必要があるんだ.-c 付きだったら,デフォルトでこう考えても良さそうなものなのに.

objdump で見てみる.

$ gobjdump a.out -t

a.out:     file format mach-o-x86-64

SYMBOL TABLE:
0000000100001000 l       0e SECT   08 0000 __DATA.__program_vars _pvars
0000000100001040 g       0f SECT   0b 0000 __DATA.__common _NXArgc
0000000100001048 g       0f SECT   0b 0000 __DATA.__common _NXArgv
0000000100001058 g       0f SECT   0b 0000 __DATA.__common ___progname
0000000100000000 g       03 ABS    01 0010 __mh_execute_header
0000000100001050 g       0f SECT   0b 0000 __DATA.__common _environ
0000000100000c50 g       0f SECT   01 0000 .text _main
0000000100000c10 g       0f SECT   01 0000 .text start
0000000000000000 g       01 UND    00 0100 _exit
0000000000000000 g       01 UND    00 0100 dyld_stub_binder

dtrace ほげほげ,というシンボルがないのが気になる.不思議.多分,objdump では読めない(対応していない)ところに,なんか情報があるんじゃないかと思うのだけど.

$ gobjdump a.out -d
(略)
0000000100000c50 <_main>:
   100000c50:   55                      push   %rbp
   100000c51:   48 89 e5                mov    %rsp,%rbp
   100000c54:   33 c0                   xor    %eax,%eax
   100000c56:   90                      nop
   100000c57:   90                      nop
   100000c58:   90                      nop
   100000c59:   85 c0                   test   %eax,%eax
   100000c5b:   74 05                   je     100000c62 <_main+0x12>
   100000c5d:   90                      nop
   100000c5e:   0f 1f 40 00             nopl   0x0(%rax)
   100000c62:   33 c0                   xor    %eax,%eax
   100000c64:   90                      nop
   100000c65:   90                      nop
   100000c66:   90                      nop
   100000c67:   85 c0                   test   %eax,%eax
   100000c69:   74 0c                   je     100000c77 <_main+0x27>
   100000c6b:   48 8d 3d 38 00 00 00    lea    0x38(%rip),%rdi        # 100000caa <_exit$stub+0x20>
   100000c72:   90                      nop
   100000c73:   0f 1f 40 00             nopl   0x0(%rax)
   100000c77:   33 c0                   xor    %eax,%eax
   100000c79:   90                      nop
   100000c7a:   90                      nop
   100000c7b:   90                      nop
   100000c7c:   85 c0                   test   %eax,%eax
   100000c7e:   74 05                   je     100000c85 <_main+0x35>
   100000c80:   90                      nop
   100000c81:   0f 1f 40 00             nopl   0x0(%rax)
   100000c85:   31 c0                   xor    %eax,%eax
   100000c87:   5d                      pop    %rbp
   100000c88:   c3                      retq
(略)

dtrace に関係ありそうなところは nop になっている.SystemTap だと,ENABLED のところはグローバル変数を読むコードが埋め込まれていたんだけど.

では,実際にどうなるのか見てみる.

$ gdb ./a.out
GNU gdb 6.3.50-20050815 (Apple version gdb-1752) (Sat Jan 28 03:02:46 UTC 2012)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin"...Reading symbols for shared li                                                                                                                                                 braries .. done

warning: UUID mismatch detected between:
        /Users/ko1/src/c/a.out
        /Users/ko1/src/c/a.out.dSYM/Contents/Resources/DWARF/a.out...

gdb を起動する../a.out をターゲットにすることを示しただけで,まだ起動はしない.

(gdb) b main
Breakpoint 1 at 0x100000c54
(gdb) run
Starting program: /Users/ko1/src/c/a.out
Reading symbols for shared libraries +........................ done

Breakpoint 1, 0x0000000100000c54 in main ()

main 関数に breakpoint を設定し,実行(run)する.プログラムは開始され,main で止まる.

では,disassemble してみる.

(gdb) disassemble
Dump of assembler code for function main:
0x0000000100000c50 <main+0>:    push   %rbp
0x0000000100000c51 <main+1>:    mov    %rsp,%rbp
0x0000000100000c54 <main+4>:    xor    %eax,%eax
0x0000000100000c56 <main+6>:    nop
0x0000000100000c57 <main+7>:    nop
0x0000000100000c58 <main+8>:    nop
0x0000000100000c59 <main+9>:    test   %eax,%eax
0x0000000100000c5b <main+11>:   je     0x100000c62 <main+18>
0x0000000100000c5d <main+13>:   nop
0x0000000100000c5e <main+14>:   nopl   0x0(%rax)
0x0000000100000c62 <main+18>:   xor    %eax,%eax
0x0000000100000c64 <main+20>:   nop
0x0000000100000c65 <main+21>:   nop
0x0000000100000c66 <main+22>:   nop
0x0000000100000c67 <main+23>:   test   %eax,%eax
0x0000000100000c69 <main+25>:   je     0x100000c77 <main+39>
0x0000000100000c6b <main+27>:   lea    0x38(%rip),%rdi        # 0x100000caa
0x0000000100000c72 <main+34>:   nop
0x0000000100000c73 <main+35>:   nopl   0x0(%rax)
0x0000000100000c77 <main+39>:   xor    %eax,%eax
0x0000000100000c79 <main+41>:   nop
0x0000000100000c7a <main+42>:   nop
0x0000000100000c7b <main+43>:   nop
0x0000000100000c7c <main+44>:   test   %eax,%eax
0x0000000100000c7e <main+46>:   je     0x100000c85 <main+53>
0x0000000100000c80 <main+48>:   nop
0x0000000100000c81 <main+49>:   nopl   0x0(%rax)
0x0000000100000c85 <main+53>:   xor    %eax,%eax
0x0000000100000c87 <main+55>:   pop    %rbp
0x0000000100000c88 <main+56>:   retq
End of assembler dump.

objdump で確認した結果と同じ.そこで,別の端末から,この a.out プロセスに対して dtrace を有効にしてみる.

$ ps ax|grep a.out
22650 s001  U+     0:00.63 /usr/libexec/gdb/gdb-i386-apple-darwin ./a.out
22662 s001  SX     0:00.21 /Users/ko1/src/c/a.out
22666 s002  R+     0:00.00 grep a.out
$ sudo dtrace -P foo22662

この foo22662 って指定がださ過ぎる.

で,もう一度 disassemble 結果を見直してみる.

(gdb) disassemble
Dump of assembler code for function main:
0x0000000100000c50 <main+0>:    push   %rbp
0x0000000100000c51 <main+1>:    mov    %rsp,%rbp
0x0000000100000c54 <main+4>:    xor    %eax,%eax
0x0000000100000c56 <main+6>:    nop
0x0000000100000c57 <main+7>:    int3
0x0000000100000c58 <main+8>:    nop
0x0000000100000c59 <main+9>:    test   %eax,%eax
0x0000000100000c5b <main+11>:   je     0x100000c62 <main+18>
0x0000000100000c5d <main+13>:   int3
0x0000000100000c5e <main+14>:   nopl   0x0(%rax)
0x0000000100000c62 <main+18>:   xor    %eax,%eax
0x0000000100000c64 <main+20>:   nop
0x0000000100000c65 <main+21>:   int3
0x0000000100000c66 <main+22>:   nop
0x0000000100000c67 <main+23>:   test   %eax,%eax
0x0000000100000c69 <main+25>:   je     0x100000c77 <main+39>
0x0000000100000c6b <main+27>:   lea    0x38(%rip),%rdi        # 0x100000caa
0x0000000100000c72 <main+34>:   int3
0x0000000100000c73 <main+35>:   nopl   0x0(%rax)
0x0000000100000c77 <main+39>:   xor    %eax,%eax
0x0000000100000c79 <main+41>:   nop
0x0000000100000c7a <main+42>:   int3
0x0000000100000c7b <main+43>:   nop
0x0000000100000c7c <main+44>:   test   %eax,%eax
0x0000000100000c7e <main+46>:   je     0x100000c85 <main+53>
0x0000000100000c80 <main+48>:   int3
0x0000000100000c81 <main+49>:   nopl   0x0(%rax)
0x0000000100000c85 <main+53>:   xor    %eax,%eax
0x0000000100000c87 <main+55>:   pop    %rbp
0x0000000100000c88 <main+56>:   retq

int3 が挿入されていることがわかる.あれあれ,dtrace の実際の実行が int3 でフックかけるのはわかるんだけど,ENABLED まで int3 で処理するの?

推測としては,int3 でカーネルに入った瞬間に当該箇所の書き換えを発生させるのかと思った(そして,以降は int3 を実行しない)のだけど,ni(gdb で,命令ごとにステップ実行)で実行しても,書き換わらなかった.もしかしたら観測していないだけかもしれないけど.

まぁ,だいたいわかった.

おまけ:

別の端末の dtrace コマンドを終了したら,ちゃんと後始末していた.正直驚いた.

(gdb) disassemble
Dump of assembler code for function main:
0x0000000100000c50 <main+0>:    push   %rbp
0x0000000100000c51 <main+1>:    mov    %rsp,%rbp
0x0000000100000c54 <main+4>:    xor    %eax,%eax
0x0000000100000c56 <main+6>:    nop
0x0000000100000c57 <main+7>:    nop
0x0000000100000c58 <main+8>:    nop
0x0000000100000c59 <main+9>:    test   %eax,%eax
0x0000000100000c5b <main+11>:   je     0x100000c62 <main+18>
0x0000000100000c5d <main+13>:   nop
0x0000000100000c5e <main+14>:   nopl   0x0(%rax)
0x0000000100000c62 <main+18>:   xor    %eax,%eax
0x0000000100000c64 <main+20>:   nop
0x0000000100000c65 <main+21>:   nop
0x0000000100000c66 <main+22>:   nop
0x0000000100000c67 <main+23>:   test   %eax,%eax
0x0000000100000c69 <main+25>:   je     0x100000c77 <main+39>
0x0000000100000c6b <main+27>:   lea    0x38(%rip),%rdi        # 0x100000caa
0x0000000100000c72 <main+34>:   nop
0x0000000100000c73 <main+35>:   nopl   0x0(%rax)
0x0000000100000c77 <main+39>:   xor    %eax,%eax
0x0000000100000c79 <main+41>:   nop
0x0000000100000c7a <main+42>:   nop
0x0000000100000c7b <main+43>:   nop
0x0000000100000c7c <main+44>:   test   %eax,%eax
0x0000000100000c7e <main+46>:   je     0x100000c85 <main+53>
0x0000000100000c80 <main+48>:   nop
0x0000000100000c81 <main+49>:   nopl   0x0(%rax)
0x0000000100000c85 <main+53>:   xor    %eax,%eax
0x0000000100000c87 <main+55>:   pop    %rbp
0x0000000100000c88 <main+56>:   retq
End of assembler dump.

参考文献


では,気になる性能を.個人的に気になるのは,トレースしない,nop 入れたバージョンですね.

ということで,今回は FOO_FOO_START_ENABLED() などだけ残したプログラムで計測します.

#include <stdio.h>
#include <stdlib.h>
#include "foo_probe.h"

int main(int argc, char *argv[]) {
  int n = 0;
  int i;
  if (argc < 2) return 0;
  n = atoi(argv[1]);
  printf("loop count: %d\n", n);

  for (i=0; i<n; i++) {
    if (FOO_FOO_START_ENABLED()) ;
    if (FOO_FOO_MIDDLE_ENABLED()) ;
    if (FOO_FOO_END_ENABLED()) ;
  }

  return 0;
}

としようとしたら,なんか怒られたので,一応最後に申し訳程度にトレースをつけておくことにする.

ちなみに,こんなふうに怒られた.なんか理不尽だ.

$ gcc -O3 foo_perf.c -o foo_perf
error: Must have a valid dtrace stability entry
ld: error creating dtrace DOF section for architecture x86_64
collect2: ld returned 1 exit status
#include <stdio.h>
#include <stdlib.h>
#include "foo_probe.h"

int main(int argc, char *argv[]) {
  int n = 0;
  int i;
  if (argc < 2) return 0;
  n = atoi(argv[1]);
  printf("loop count: %d\n", n);

  for (i=0; i<n; i++) {
    if (FOO_FOO_START_ENABLED()) ;
    if (FOO_FOO_MIDDLE_ENABLED()) ;
    if (FOO_FOO_END_ENABLED()) ;
  }


  FOO_FOO_START();
  FOO_FOO_MIDDLE("foo!");
  FOO_FOO_END();
  return 0;
}

まずは,普通にビルドして,実行.

結果:

$ time ./foo_perf 1000000000
loop count: 1000000000

real    0m1.760s
user    0m1.756s
sys     0m0.003s

次に,dtrace を有効にして実行.

$ time sudo dtrace -c './foo_perf 1000000000' -P 'foo$target'
dtrace: description 'foo$target' matched 3 probes
loop count: 1000000000

お,終わらない....10倍程度は覚悟していたんだが.やっぱり,int3 でやってるから悪いのかなぁ.

ちょっと,数を減らす.

$ time ./foo_perf 10000000
loop count: 10000000

real    0m0.026s
user    0m0.022s
sys     0m0.003s
$ time sudo dtrace -c './foo_perf 10000000' -P 'foo$target'
dtrace: description 'foo$target' matched 3 probes
loop count: 10000000
dtrace: pid 22947 has exited
CPU     ID                    FUNCTION:NAME
  2 145289                   main:foo_start
  2 145288                  main:foo_middle
  2 145287                     main:foo_end


real    0m15.565s
user    0m10.090s
sys     0m5.452s

なんというか,有効にしなかったときの速度がさっぱり信用ならない(多分,プロセス生成の時間が大幅にくってんだろう)ので確かなことは言えないのだが,dtrace を有効にするというのは,まぁそれなりにコストがかかるということはわかった.まぁ,int3 だしなあ.


さて,個人的に一番知りたい ENABLED() のコストについて.つまり,グローバル変数をチェックするのと,nop で埋めるの,どっちが速いか,という話.

では,無効にしてみよう.

$ gcc -O3 foo_perf.c -o foo_perf_disabled -D DTRACE_PROBES_DISABLED=1

DTRACE_PROBES_DISABLED を 1 にしておけば,dtrace 回りは 0 になる.ついでに,ループが消えないように asm volatile(""); を突っ込んでおいた.

0000000100000ed0 <_main>:
   100000ed0:   55                      push   %rbp
   100000ed1:   48 89 e5                mov    %rsp,%rbp
   100000ed4:   53                      push   %rbx
   100000ed5:   48 83 ec 08             sub    $0x8,%rsp
   100000ed9:   83 ff 02                cmp    $0x2,%edi
   100000edc:   7c 26                   jl     100000f04 <_main+0x34>
   100000ede:   48 8b 7e 08             mov    0x8(%rsi),%rdi
   100000ee2:   e8 27 00 00 00          callq  100000f0e <_atoi$stub>
   100000ee7:   89 c3                   mov    %eax,%ebx
   100000ee9:   48 8d 3d 5e 00 00 00    lea    0x5e(%rip),%rdi        # 100000f4e <_printf$stub+0x34>
   100000ef0:   30 c0                   xor    %al,%al
   100000ef2:   89 de                   mov    %ebx,%esi
   100000ef4:   e8 21 00 00 00          callq  100000f1a <_printf$stub>
   100000ef9:   85 db                   test   %ebx,%ebx
   100000efb:   7e 07                   jle    100000f04 <_main+0x34>
   100000efd:   0f 1f 00                nopl   (%rax)
   100000f00:   ff cb                   dec    %ebx
   100000f02:   75 fc                   jne    100000f00 <_main+0x30>
   100000f04:   31 c0                   xor    %eax,%eax
   100000f06:   48 83 c4 08             add    $0x8,%rsp
   100000f0a:   5b                      pop    %rbx
   100000f0b:   5d                      pop    %rbp
   100000f0c:   c3                      retq

よくわかんないけど,多分ループ残ってるんじゃないだろうか.では,いざ実行.

$ time ./foo_perf 1000000000
loop count: 1000000000

real    0m1.760s
user    0m1.756s
sys     0m0.003s
$ time ./foo_perf_disabled 1000000000
loop count: 1000000000

real    0m0.448s
user    0m0.444s
sys     0m0.003s

だいたい3倍? 3個 probe が入ってるから,まぁそんなもんか.nop が入ってるだけでも馬鹿に出来ないところは馬鹿に出来ないのだねえ.

では,dtrace 環境がないところで dtrace みたいなことをやるときの常道,グローバル変数でチェックするのはどうか.

#include <stdio.h>
#include <stdlib.h>
#include "foo_probe.h"

#define nop() asm volatile("")
int probe_p[3];

int main(int argc, char *argv[]) {
  int n = 0;
  int i;
  if (argc < 2) return 0;
  n = atoi(argv[1]);
  printf("loop count: %d\n", n);

  for (i=0; i<n; i++) {
    nop();
#if !DTRACE_PROBES_DISABLED
    if (FOO_FOO_START_ENABLED()) nop();
    if (FOO_FOO_MIDDLE_ENABLED()) nop();
    if (FOO_FOO_END_ENABLED()) nop();
#else
    if (probe_p[0]) nop();
    if (probe_p[1]) nop();
    if (probe_p[2]) nop();
#endif
  }


  FOO_FOO_START();
  FOO_FOO_MIDDLE("foo!");
  FOO_FOO_END();

  return 0;
}

こんなコードにしてみた.

$ gcc -O3 foo_perf.c -o foo_perf
$ gcc -O3 foo_perf.c -o foo_perf_disabled -D DTRACE_PROBES_DISABLED=1
$ time ./foo_perf_disabled 1000000000
loop count: 1000000000

real    0m1.765s
user    0m1.760s
sys     0m0.003s
$ time ./foo_perf 1000000000
loop count: 1000000000

real    0m3.515s
user    0m3.511s
sys     0m0.003s

上がグローバル変数版,下が dtrace 版.あれあれー.そもそも dtrace 版の値が変わっている....nop() って付けたからかな.そんな気がする.

そして,驚くべきは,グローバル変数版のほうが速いってこと.これは意外.いや,そうでもないのか? 分岐予測が十分に当たるときは,nop で埋められるよりも速いってことなのだろう.

評価環境:

  • Darwin 11.4.0 Darwin Kernel Version 11.4.0: Mon Apr 9 19:32:15 PDT 2012; root:xnu-1699.26.8~1/RELEASE_X86_64 x86_64
  • machdep.cpu.brand_string: Intel(R) Core(TM) i5-2467M CPU @ 1.60GHz

一応確認.グローバル変数版のコンパイル結果:

0000000100000eb0 <_main>:
   100000eb0:   55                      push   %rbp
   100000eb1:   48 89 e5                mov    %rsp,%rbp
   100000eb4:   53                      push   %rbx
   100000eb5:   48 83 ec 08             sub    $0x8,%rsp
   100000eb9:   83 ff 02                cmp    $0x2,%edi
   100000ebc:   7c 47                   jl     100000f05 <_main+0x55>
   100000ebe:   48 8b 7e 08             mov    0x8(%rsi),%rdi
   100000ec2:   e8 47 00 00 00          callq  100000f0e <_atoi$stub>
   100000ec7:   89 c3                   mov    %eax,%ebx
   100000ec9:   48 8d 3d 7e 00 00 00    lea    0x7e(%rip),%rdi        # 100000f4e <_printf$stub+0x34>
   100000ed0:   30 c0                   xor    %al,%al
   100000ed2:   89 de                   mov    %ebx,%esi
   100000ed4:   e8 41 00 00 00          callq  100000f1a <_printf$stub>
   100000ed9:   85 db                   test   %ebx,%ebx
   100000edb:   7e 28                   jle    100000f05 <_main+0x55>
   100000edd:   48 8d 05 8c 01 00 00    lea    0x18c(%rip),%rax        # 100001070 <_probe_p>
   100000ee4:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
   100000eea:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
   100000ef0:   83 38 00                cmpl   $0x0,(%rax)
   100000ef3:   74 00                   je     100000ef5 <_main+0x45>
   100000ef5:   83 78 04 00             cmpl   $0x0,0x4(%rax)
   100000ef9:   74 00                   je     100000efb <_main+0x4b>
   100000efb:   83 78 08 00             cmpl   $0x0,0x8(%rax)
   100000eff:   74 00                   je     100000f01 <_main+0x51>
   100000f01:   ff cb                   dec    %ebx
   100000f03:   75 eb                   jne    100000ef0 <_main+0x40>
   100000f05:   31 c0                   xor    %eax,%eax
   100000f07:   48 83 c4 08             add    $0x8,%rsp
   100000f0b:   5b                      pop    %rbx
   100000f0c:   5d                      pop    %rbp
   100000f0d:   c3                      retq

ちゃんとチェックしてる気がする.

dtrace 版.

0000000100000bf0 <_main>:
   100000bf0:   55                      push   %rbp
   100000bf1:   48 89 e5                mov    %rsp,%rbp
   100000bf4:   53                      push   %rbx
   100000bf5:   48 83 ec 08             sub    $0x8,%rsp
   100000bf9:   83 ff 02                cmp    $0x2,%edi
   100000bfc:   7c 57                   jl     100000c55 <_main+0x65>
   100000bfe:   48 8b 7e 08             mov    0x8(%rsi),%rdi
   100000c02:   e8 57 00 00 00          callq  100000c5e <_atoi$stub>
   100000c07:   89 c3                   mov    %eax,%ebx
   100000c09:   48 8d 3d 8e 00 00 00    lea    0x8e(%rip),%rdi        # 100000c9e <_printf$stub+0x34>
   100000c10:   30 c0                   xor    %al,%al
   100000c12:   89 de                   mov    %ebx,%esi
   100000c14:   e8 51 00 00 00          callq  100000c6a <_printf$stub>
   100000c19:   85 db                   test   %ebx,%ebx
   100000c1b:   7e 22                   jle    100000c3f <_main+0x4f>
   100000c1d:   0f 1f 00                nopl   (%rax)
   100000c20:   33 c0                   xor    %eax,%eax
   100000c22:   90                      nop
   100000c23:   90                      nop
   100000c24:   90                      nop
   100000c25:   85 c0                   test   %eax,%eax
   100000c27:   74 00                   je     100000c29 <_main+0x39>
   100000c29:   33 c0                   xor    %eax,%eax
   100000c2b:   90                      nop
   100000c2c:   90                      nop
   100000c2d:   90                      nop
   100000c2e:   85 c0                   test   %eax,%eax
   100000c30:   74 00                   je     100000c32 <_main+0x42>
   100000c32:   33 c0                   xor    %eax,%eax
   100000c34:   90                      nop
   100000c35:   90                      nop
   100000c36:   90                      nop
   100000c37:   85 c0                   test   %eax,%eax
   100000c39:   74 00                   je     100000c3b <_main+0x4b>
   100000c3b:   ff cb                   dec    %ebx
   100000c3d:   75 e1                   jne    100000c20 <_main+0x30>
   100000c3f:   90                      nop
   100000c40:   0f 1f 40 00             nopl   0x0(%rax)
   100000c44:   48 8d 3d 63 00 00 00    lea    0x63(%rip),%rdi        # 100000cae <_printf$stub+0x44>
   100000c4b:   90                      nop
   100000c4c:   0f 1f 40 00             nopl   0x0(%rax)
   100000c50:   90                      nop
   100000c51:   0f 1f 40 00             nopl   0x0(%rax)
   100000c55:   31 c0                   xor    %eax,%eax
   100000c57:   48 83 c4 08             add    $0x8,%rsp
   100000c5b:   5b                      pop    %rbx
   100000c5c:   5d                      pop    %rbp
   100000c5d:   c3                      retq

ふーむ.やっぱり違いはメモリアクセスか nop か,ってことになるようだ.面白いな.

まぁ,この例だとキャッシュにあたるしねぇ.


solaris だともっと賢いことしてそうだなあ.

_10(Thu)

nyaxt に教えてもらった:http://www.7-cpu.com/cpu/SandyBridge.html

最近の CPU って速いねえ.

_6(Sun)

  • 4/28 (土) ニコニコ超会議,六本木で飲み会
  • 4/29 (日) 寝てた
  • 4/30 (月) 寝てた
  • 5/1 (火) 職場で何か
  • 5/2 (水) 職場でmruby眺めた
  • 5/3 (木) 雨なので家で寝てた
  • 5/4 (金) 雨なので家で寝てた
  • 5/5 (土) 自転車で外出(午前) 昼は寝てた
  • 5/6 (日) 雷雨なので家で寝てた

なんという,生産性のない.というか,寝過ぎ.しかも風邪引いた.


Ubuntu 環境がよくわからない.samba インストールしたら [homes] がデフォルトでオンになっておらずはまる./init.d/foo を直接触るんじゃなくて,service foo restart みたいにするんだな.

Sasada Koichi / ko1 at atdot dot net
$Date: 2003/04/28 10:27:51 $