2013-10-10

Gentooを最速でブートせよ

Patrick's playground: October 2013 Archives

KVM上のVMで、Gentooをひたすら短時間でブートして、haltさせる試み。

BOOTING FAST(ER)

(より)高速に起動

本日、筆者は積年の疑問を解決すべく遊んだ。どのくらい速く、KVM上のVMでブートして、haltできるのか。

そこで、この実験のため、CPUの速度を最低の1.4GHzにした。そうでなければ面白くないだろう。目標は、KVMのVM上のGentoo/amd64を、十分に短い時間でブートして、haltすることだ。

rootファイルシステムはsquashfsにした。最初に行った1GBのext4ファイルシステム vs squashfsでは、fsck+mountというありがた迷惑のため、5秒の差がでたからだ。うへぇ。stage3を展開し、いくつか設定をして(デバッグのためにログインしたかったので、パスワードを設定などした)、squashfsを以下のように構築した。

mksquashfs stage3/* ./kvm-squashfs -comp lzo

カーネルは最小に削ぎ落した。virtioドライバーを使い、他はすべて削ぎ落とし、必要の無さそうなものを次第次第にそぎ落としていった。

Setup is 15264 bytes (padded to 15360 bytes).
System is 3004 kB
CRC 7315b3d8
Kernel: arch/x86/boot/bzImage is ready  (#8)

まだちょっとデカすぎる。これでも何度目かの削ぎ落とし試行をした結果なのだ。

最初の挑戦では、7.5秒ほどに計測された。

qemu-kvm -nographic  -kernel kernel  -boot c -drive file=./kvm-squashfs,if=virtio -append "quiet root=/dev/vda console=ttyS0 init=/sbin/halt"
[    7.409846] reboot: Power down

悪かぁないが・・・よくもない。明らかに、遅くてデブいのはbashだ。そこで、busyboxのshを使うことにした。

[    5.709225] reboot: Power down

おおっと、こいつは驚き桃の木山椒の木。

他に驚いたこととしては、"-smp 4"でブートすると、0.3秒ほどカーネル時間を食う。パラレルブートすると、0.2秒遅くなる。ハァッ?

mdevとudevの違いは誤差の範囲だ。mdevのほうがわずかに早いようにみえる。

メモリーを追加すると( -m 1024 )、0.2秒ほどスピードアップする。

ブートパラメーター"quiet"で、0.1秒ほど稼げる。

しょうもないサービスが山ほどある。削ってしまおう。

# ls /etc/runlevels/*

etc/runlevels/boot:
bootmisc  hostname  localmount  mtab  net.lo  procfs  sysctl  tmpfiles.setup

etc/runlevels/default:
local  netmount

etc/runlevels/shutdown:
killprocs

etc/runlevels/sysinit:
dmesg  mdev  sysfs

さて、不必要なものが山ほどある(keymap? キーボードなんざお呼びじゃねェぜッ!)ので削れる。(root? squashfsだから、/をrwで再マウントなんかできねェぜッ! urandom? そもそも乱数seedなんて保持できねーのによ。等など)。結果? かろうじて5秒以下になった。

[    4.966840] reboot: Power down

しかし、ここで筆者は気づいてしまう・・・まてよ・・・最後のkillprocsが、「永遠」みたいに時間かかるぞ。何でだと思う。実は・・・二回もの・・・"sleep 1"

なんという時間の無駄遣いだ。消せ消せ。

[    2.798762] reboot: Power down

やったぜ。開始から停止まで3秒以下だ。

続き

BOOTING SERIOUSLY FAST NOW

マジで高速に起動

KVMのインスタンスをブートしてシャットダウンまで:

[    0.653860] reboot: Power down

やってやったぜ。筆者は前回の2.5秒の記録を劇的に破ってみせたぜ。何があった?

スタートアップはもうマジで速くなったのだが、シャットダウンが、永遠に激遅だった。不思議なことだ。そこで、sysvinitを読んで、いったいstop/rebootで何が起こっているのか調べてみた。興味深い部分が、/usr/include/sys/reboot.hにあった。

/* Stop system and switch power off if possible.  */
#define RB_POWER_OFF    0x4321fedc

マジックナンバーがシステムコールに渡されて、カーネルに「止まりやがれ」と命じる。

sysvinitのソースを更に掘り進めていくと、筆者が気がついたのは・・・えーっと・・・ハァッ?・・・ハァァァッ?

src/halt.c ~line 266:

        if (do_sync) {
                sync();
                sleep(2);
        }

というわけでですな、場合によってはですな、つまり、その、オホン、2秒もオネンネかよ!!!!!

(この時点で、全体のブートサイクルは2.5秒であることをお忘れなく)

do_syncをセットするのが何であるかを調べたあと(-nオプションだったよ)、とりあえず/etc/inittabに設定してみた。

-l0s:0:wait:/sbin/halt -dhp
+l0s:0:wait:/sbin/halt -dhpn

どうだ。開始から停止までのサイクルが1秒未満。

とりあえず記録のために書いておくと、7.5秒から0.7秒に縮めるために、4秒のディレイを発生させるsleep()を3個取り除き、busyboxのshとは違いクソ遅いbashによって「浪費」されていた2秒を稼いだ。

どうやら、このあたりをプロファイルする奴は、ここ10年ほどいなかったようだなw

更に続き

FASTER BOOT AGAIN NOW

またまた高速化ブート

[    0.338524] reboot: Power down

さて、もういい加減にバカバカしくなってくる。

これは、net.loを開始しないために速くなってるのが大きい。それと、必須ではないinit scriptを全部取り除いた。どうもパースするオーバーヘッド(あるいは、単にひとつのディレクトリ下により多くのファイルが存在することに起因する遅さ)というのが、このレベルになると無視できなくなってくるのだ。

で、何が起こっているのか。

カーネルがブート
rootfsのマウントされる
その他のファイルシステム(proc, sys, dev, run ...)がマウントされる
udev/mdevが開始

この後に、ログイン可能になる。電源投入から約250ミリ秒後だ。(あるいは、kvmの開始から一秒後、kvmも無視できないほどの初期化時間がかかる)

これはもはや何の役にもたたないと言われるかもしれないが、これは単に、高速にブートするということであって、他の何かをするのではないのだ。

No comments: