サーバ構築メモ書き-4

新しいサーバにはRHEL5をインストールしたので、Movable Typeのバックエンドには4つの選択肢がある(SQLiteとかなし。Movable Type以外にもDBを使うので)。つまり、

  • RHELに含まれるmysql-server-5.0.45-7.el5
  • RHELに含まれるpostgresql-server-8.1.11-1.el5_1.1

が標準的な選択肢なのだけれど、

  • Red Hat Application Stackに含まれるmysql-server-5.0.50sp1a-2.el5s2
  • Red Hat Application Stackに含まれるpostgresql-server-8.2.9-1.el5s2

だ。後者は追加でサブスクリプション費用が発生するけれど、httpdもRHEL5のhttpd-2.2.3-11.el5_2.4に対してhttpd-2.2.8-1.el5s2になるし、JBossとか入ってる。何よりRed Hatが提供するコミュニティ版に近いバージョンということで管理がしやすい。

さて。
MySQLは同じ5.0系なのでパフォーマンスはそれほど変わらないにしても、PostgreSQLの8.1と8.2ではだいぶ違うはずだ。もちろん、MySQLからPostgreSQLに乗り換えるなら、単にSunがダメダメだという以上の理由が欲しい。個別にベンチマークをとっても単一の基準にならないので、ここは一つ、本題であるMovable Typeの再構築時間を計ってみようと考えた。

そこで、mt-rebuildをtimeコマンドで計測してみよう。
まず計測条件だけれど、ハードウェアは先に書いた通り。関連するファイルは全てIntel SSDの上に存在する。
MTのエントリー数は4681ある。
MySQLとPostgreSQLのパラメータはデフォルトと、このあたりがベストだろうというパラメータの2通り。つまり、以下の組み合わせとする。

  • mysql-server-5.0.45-7.el5
    • Default-pattern1
    • Tuned-pattern2
  • postgresql-server-8.1.11-1.el5_1.1
    • Default-pattern3
    • Tuned-pattern4
  • mysql-server-5.0.50sp1a-2.el5s2
    • Default-pattern5
    • Tuned-pattern6
  • postgresql-server-8.2.9-1.el5s2
    • Default-pattern7
    • Tuned-pattern8

pattern1

# time ./mt-rebuild.pl --all
real	6m35.778s
user	3m21.848s
sys	0m17.918s

この値は既に現在のサーバの約2倍という性能が出ているのだけれど、もちろん、そんなことでは終われない。

実はこの後、pattern2をやってみて、ほとんど同じ結果が出た。何パターンかmy.cnfをいじくってみたんだけれど、結果は変わらず…。
少し考えてみた結果、この再構築にかかっている時間の大半はmysqlではなく、他の要因があるんじゃないかと。ボトルネックを探すことに作戦を変更。

oprofileを使って、どこにどのくらい時間がかかっているのか調べてみよう。

まず下準備として、kernel-debuginfoとkernel-debuginfo-commonが必要。これはftp.redhat.comにあるのでダウンロードしてインストールする。yumでoprofileをインストールして、mt-rebuild.plを1つのファイルにだけ実行してみる。

# opcontrol --start;./mt-rebuild.pl -mode=index -blog_id=2 -template="メインページ"; opcontrol --stop
Profiler running.
Stopping profiling.

oprofileの設定を変えたり、例えば、perl-debuginfoを追加インストールした時は、次のようにリセットしておく。

# opcontrol --reset

なんだか思ったように動いてくれない時は、念のため次も。

# opcontrol --deinit# opcontrol --init

プロファイルの結果を見てみよう。

CPU: Core 2, speed 2003 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples  %        app name                 symbol name
53421    66.9965  libperl.so               (no symbols)
4959      6.2192  mysqld                   (no symbols)
1840      2.3076  libc-2.5.so              _int_malloc
990       1.2416  vmlinux                  clear_page
859       1.0773  libc-2.5.so              memcpy
794       0.9958  vmlinux                  .text.acpi_processor_idle
......

やっぱり。mysqlじゃなくてperlか…。perlはどんな風にコンパイルされてるのかというと、

# perl -V
Summary of my perl5 (revision 5 version 8 subversion 8) configuration:
......
  Compiler:
    cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
    optimize='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic',
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -I/usr/include/gdbm'

-m64と-mtune=genericだ。ところが、

# gcc --target-help
......
-march=CPU/-mtune=CPU   generate code/optimize for CPU, where CPU is one of:
                           i386, i486, pentium, pentiumpro, pentium4, nocona,
			   core, core2, k6, athlon, k8, generic32, generic64

とのことらしく、core2への最適化が可能らしい。うーん、この-m64と-mtune=genericはどこで渡しているんだろうと思い、perlのSRPMを持ってきて、specやらMakefileやらConfigureやらconfigure*やら見るけど分からず…。
specを見るとRPM_OPT_FLAGSが怪しいのだけれど、出所が分からない。

445 sh Configure -des -Doptimize="$RPM_OPT_FLAGS" \
446         -Dversion=%{perlver} \
447         -Dmyhostname=localhost \
448         -Dperladmin=root@localhost \
449         -Dcc='%{__cc}' \
450         -Dcf_by='Red Hat, Inc.' \
451         -Dinstallprefix=%{_prefix} \
452         -Dprefix=%{_prefix} \
453 %ifarch %{multilib_64_archs}
454         -Dlibpth="/usr/local/lib64 /lib64 /usr/lib64" \

ここでふと思いつく。これはperlパッケージ特有の最適化フラグではなくて、RHELのx86_64に共通のオプションじゃないのかと。rpmbuildにstraceをかけてみる。

# strace -e open rpmbuild -bb /usr/src/redhat/SPECS/perl.spec
open("/etc/ld.so.cache", O_RDONLY)      = 3
open("/usr/lib64/librpmbuild-4.4.so", O_RDONLY) = 3
open("/usr/lib64/librpm-4.4.so", O_RDONLY) = 3
open("/usr/lib64/librpmdb-4.4.so", O_RDONLY) = 3
open("/lib64/libselinux.so.1", O_RDONLY) = 3
open("/usr/lib64/librpmio-4.4.so", O_RDONLY) = 3
open("/usr/lib64/libpopt.so.0", O_RDONLY) = 3
open("/usr/lib64/libsqlite3.so.0", O_RDONLY) = 3
open("/usr/lib64/libelf.so.1", O_RDONLY) = 3
open("/usr/lib64/libbeecrypt.so.6", O_RDONLY) = 3
open("/lib64/libm.so.6", O_RDONLY)      = 3
open("/lib64/libdl.so.2", O_RDONLY)     = 3
open("/usr/lib64/libz.so.1", O_RDONLY)  = 3
open("/lib64/librt.so.1", O_RDONLY)     = 3
open("/lib64/libpthread.so.0", O_RDONLY) = 3
open("/usr/lib64/libbz2.so.1", O_RDONLY) = 3
open("/lib64/libc.so.6", O_RDONLY)      = 3
open("/lib64/libsepol.so.1", O_RDONLY)  = 3
open("/lib64/libgcc_s.so.1", O_RDONLY)  = 3
open("/usr/lib64/libstdc++.so.6", O_RDONLY) = 3
open("/etc/selinux/config", O_RDONLY)   = 3
open("/proc/mounts", O_RDONLY)          = 3
open("/selinux/mls", O_RDONLY)          = 3
open("/usr/lib/locale/locale-archive", O_RDONLY) = 3
open("/usr/lib/rpm/rpmpopt-4.4.2", O_RDONLY) = 3
open("/etc/popt", O_RDONLY)             = -1 ENOENT (No such file or directory)
open("/root/.popt", O_RDONLY)           = -1 ENOENT (No such file or directory)
open("/etc/rpm/platform", O_RDONLY)     = 3
open("/usr/lib/rpm/rpmrc", O_RDONLY)    = 3
open("/usr/lib/rpm/redhat/rpmrc", O_RDONLY) = 3
......

/usr/lib/rpm/以下に.soじゃないファイルがあり、それをopenしていることに気付く。ん〜?

# grep -r 'RPM_OPT_FLAGS' /usr/lib/rpm/*
/usr/lib/rpm/brp-java-gcjcompile:cflags="$RPM_OPT_FLAGS -fPIC -findirect-dispatch"
/usr/lib/rpm/macros:  RPM_OPT_FLAGS=\"%{optflags}\"\
/usr/lib/rpm/macros:  export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\
/usr/lib/rpm/redhat/macros:RPM_OPT_FLAGS=\"%{optflags}\"\
/usr/lib/rpm/redhat/macros:export RPM_SOURCE_DIR RPM_BUILD_DIR RPM_OPT_FLAGS RPM_ARCH RPM_OS\
/usr/lib/rpm/rpmrc:# Values for RPM_OPT_FLAGS for various platforms
......

ビンゴ…。

# vi /usr/lib/rpm/redhat/rpmrc
1 include: /usr/lib/rpm/rpmrc
2
3 optflags: i386 %{__global_cflags} -m32 -march=i386 -mtune=generic -fasynchronous-unwind-tables
4 optflags: i486 %{__global_cflags} -m32 -march=i486 -fasynchronous-unwind-tables
5 optflags: i586 %{__global_cflags} -m32 -march=i586 -fasynchronous-unwind-tables
6 optflags: i686 %{__global_cflags} -m32 -march=i686 -mtune=generic -fasynchronous-unwind-tables
7 optflags: athlon %{__global_cflags} -m32 -march=athlon -fasynchronous-unwind-tables
8 optflags: ia64 %{__global_cflags}
9 optflags: x86_64 %{__global_cflags} -m64 -mtune=generic

変更してrpmbuildしてみた。

# time ./mt-rebuild.pl --all
real	6m34.897s
user	3m19.360s
sys	0m17.696s

変わらず(/_;)

つまり、Core2Duoのレジスタとか、別に関係無いってことか。ちょっとしつこく食い下がってみる。perl-debuginfoを投入して、oprofile。

CPU: Core 2, speed 2003 MHz (estimated)
Counted CPU_CLK_UNHALTED events (Clock cycles when not halted) with a unit mask of 0x00 (Unhalted core cycles) count 100000
samples  %        app name                 symbol name
1887      6.9941  libperl.so               S_hv_fetch_common
1654      6.1305  mysqld                   (no symbols)
665       2.4648  libperl.so               Perl_pp_entersub
605       2.2424  libperl.so               Perl_yyparse
598       2.2165  libc-2.5.so              _int_malloc
587       2.1757  libperl.so               .plt
561       2.0793  libperl.so               Perl_leave_scope
539       1.9978  libperl.so               Perl_sv_setsv_flags
522       1.9348  libperl.so               Perl_yylex
505       1.8718  libperl.so               Perl_pp_padsv
394       1.4603  libperl.so               Perl_sv_clear
383       1.4196  libperl.so               Perl_gv_fetchpv
369       1.3677  vmlinux                  .text.acpi_processor_idle
357       1.3232  libperl.so               Perl_sv_upgrade
337       1.2491  libperl.so               Perl_pp_nextstate
329       1.2194  vmlinux                  clear_page
316       1.1712  libperl.so               Perl_runops_standard
282       1.0452  libc-2.5.so              memcpy
274       1.0156  libperl.so               Perl_sv_free
259       0.9600  libperl.so               S_regmatch
258       0.9563  libc-2.5.so              memmove
254       0.9414  libperl.so               S_share_hek_flags
214       0.7932  libperl.so               Perl_pp_rv2av
207       0.7672  libperl.so               Perl_pp_helem
202       0.7487  libc-2.5.so              malloc
199       0.7376  libperl.so               Perl_pp_pushmark
198       0.7339  libperl.so               Perl_sv_gets
191       0.7079  libperl.so               S_method_common
187       0.6931  vmlinux                  page_fault
186       0.6894  libperl.so               Perl_peep
177       0.6560  libperl.so               Perl_pp_rv2hv
162       0.6004  libperl.so               Perl_pp_return
158       0.5856  libperl.so               Perl_pp_const
157       0.5819  libperl.so               Perl_pp_and
156       0.5782  libc-2.5.so              free
149       0.5523  libc-2.5.so              _int_free
148       0.5486  libperl.so               Perl_pp_leavesub
140       0.5189  libperl.so               Perl_scalar

あー、今日はここまで。