新しいサーバには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
あー、今日はここまで。