More info...

2010-04-22

貧乏だってプロファイリングは出来る!! - poor man's profiler

従来より、プロファイリングのためのソフトウェアと言えば高価なものが中心であった。もっと安く、お金を掛けずに、簡単に、早くプログラムのボトルネックを探し出す方法はないのか?!ということで編み出されたプロファイリングテクノロジーがある。その名も、「poor man's profiler」だ。

poor man's profilerの全容は、次のページで知ることが出来る。

Poor Man's Profiler
http://poormansprofiler.org/

poor man's profilerは、現Facebook(元MySQL ABのサポートエンジニア)のDomas Mituzasによって開発されたプロファイリングテクノロジーである。以下が、その全ソースコードである。
#!/bin/bash
nsamples=1
sleeptime=0
pid=$(pidof mysqld)

for x in $(seq 1 $nsamples)
  do
    gdb -ex "set pagination 0" -ex "thread apply all bt" -batch -p $pid
    sleep $sleeptime
  done | \
awk '
  BEGIN { s = ""; } 
  /Thread/ { print s; s = ""; } 
  /^\#/ { if (s != "" ) { s = s "," $4} else { s = $4 } } 
  END { print s }' | \
sort | uniq -c | sort -r -n -k 1,1
何を言っているのか分からないようなのでもう一度言う。これが、poor man's profilerのソースコードの全てである。

「馬鹿にするな!こんなたった10数行程度のプログラムで、プロファイリングなんて出来るわけがないだろっ!」

などと思ったアナタ。まずはこのプロファイラの出力サンプルを見て欲しい。
shell> sudo poormans-profiler -p 1593
     50 pthread_cond_wait@@GLIBC_2.3.2,os_event_wait_low,srv_conc_enter_innodb,ha_innobase::general_fetch(unsigned,rr_sequential(st_read_record*),sub_select(JOIN*,,??,JOIN::exec(),mysql_select(THD*,,handle_select(THD*,,??,mysql_execute_command(THD*),mysql_parse(THD*,,dispatch_command(enum_server_command,,do_command(THD*),handle_one_connection,start_thread,clone,??
     11 select,os_thread_sleep,srv_conc_enter_innodb,ha_innobase::general_fetch(unsigned,rr_sequential(st_read_record*),sub_select(JOIN*,,??,JOIN::exec(),mysql_select(THD*,,handle_select(THD*,,??,mysql_execute_command(THD*),mysql_parse(THD*,,dispatch_command(enum_server_command,,do_command(THD*),handle_one_connection,start_thread,clone,??
      5 __lll_lock_wait,_L_lock_1173,__pthread_mutex_lock,??,??,ha_autocommit_or_rollback(THD*,,dispatch_command(enum_server_command,,do_command(THD*),handle_one_connection,start_thread,clone,??
      4 pthread_cond_wait@@GLIBC_2.3.2,os_event_wait_low,os_aio_simulated_handle,fil_aio_wait,??,start_thread,clone,??
      3 select,os_thread_sleep,srv_conc_enter_innodb,ha_innobase::write_row(unsigned,handler::ha_write_row(unsigned,write_record(THD*,,mysql_insert(THD*,,mysql_execute_command(THD*),mysql_parse(THD*,,dispatch_command(enum_server_command,,do_command(THD*),handle_one_connection,start_thread,clone,??
            :
          (snip)
これは何をしているかというと、gdbでスレッドの一覧を抜き出し、awkで整形し、sortで集計しているのである。これにより、スレッドがどの関数を実行しているか、即ちどのロックを待っているかということが統計的に分かるのだ!!この例では、50のスレッドがos_event_wait_low()内でpthread_cond_wait()していることが分かる。このような情報は、まさにシングルプロセス/マルチスレッドプログラムにおいて、ボトルネックになり得るロックを探し出すのに打ってつけだ。

もちろん、「スレッドの実行がどこで停止しているか」ということが分かったからと言って、それが即解決に繋がるわけではない。ボトルネックを解消するには、なぜ多くのスレッドがそのロックを待っているかということを分析し、仮説を立て、解決法を実装し、効果を測定するという地道な作業を積み重ねる必要があるだろう。ボトルネックを解消するのは一筋縄では行かない。Twitterの中の人も言っているが、チューニングに定石はなく、「すべてのエンジニアリング上の解決策というのは、一時的なもの」なのだ。だが、少なくともこれだけは言える。poor man's profilerはボトルネックを発見する人にとって力強い味方なのだ!!

poor man's profilerは、UNIXで広く使われている汎用ツール3つを駆使している。使われているコマンドは、
  • gdb
  • awk
  • sort
  • uniq
だけである。これらのツールは誰でも使えるものであり、全てタダ。安価なネットブックであっても、Linuxをインストールすれば使うことが出来るものばかりである。

poor man's profilerを使うのに金はかからない。その上仕組みは至極簡単だ。しかし効果は絶大である。たとえ貧乏でもプロファイリングが出来るのである!!マルチスレッドプログラムで思うように性能が出ないと感じたら、まずはpoor man's profilerのテクニックを試して頂きたい。

poor man's profilerのスクリプトは至極単純だが、いちいち書き換えるのは面倒だ。そもそもマルチスレッドプログラムであれば汎用的に利用出来るテクニック(もちろんMySQL以外のプログラムにも!)であるため、スクリプト化してオプションを受け付けた方が便利である。そこで、そのように仕立てたスクリプトを書いたので、ここからダウンロードして利用して頂きたい。このスクリプトの書式とオプションの意味は次の通り。
shell> poormans-profiler -p {pid} [options]
or
shell> poormans-profiler [options] {progname}
  • -p: プロファイリング対象プロセスのPID
  • -n: 情報をサンプリングする回数
  • -s: サンプリングする間隔(秒)
  • -w: 最大文字幅(デフォルトは999)
貧乏人には時間がない。食べ物が必要だ。だからこのプロファイラを使って短時間で良い仕事をして、お金を稼いで美味いものを食おう!!

0 件のコメント:

コメントを投稿