シンボリックリンクのパフォーマンスが気になった
2018/03/05
便利な便利なシンボリックリンク、その用途はWebサイトのホットデプロイやらMuninのプラグインやら、実に多岐にわたります。(個人的にはハードリンクの方が好きですけど……)
ところでこれ、便利ではあるのですが、要はリンク先を読み取ってから更に実ファイルにアクセスする訳です。
性能厨の私として、ふと「オーバーヘッドがあるのでは?」と気になったため調べてみました。
スポンサーリンク
あ、そうそう、Linux(ext4)大前提でお話ししていますよ。
他のファイルシステムだと変わって来るかもです。
テスト環境
テスト環境として仮想環境(VirtualBox)のUbuntu 16.04を用意しました。
仮想環境だとディスクアクセスが遅かったりするのですが、今回調べたいのはシンボリックリンクにオーバーヘッドがあるのか?なのでその辺りの細かい点は気にしなくてもよいかと……
測定方法
空のファイルが9999個ある(touch {1..9999}で作成)ディレクトリA、及びそれのシンボリックリンク版のディレクトリBに対して以下の様なコマンドを発行します。
time ls -l ./a
キャッシュなんかを想定して、実行の度に以下のコマンドでキャッシュを解放します。
sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"
測定
と言う訳で実際に試していきましょい!!
実際のファイル
まずは実際のファイルに対して実行しましょい!!
~$ time ls -l ./a
~~~ 略 ~~~
-rw-rw-r-- 1 test test 0 11月 24 22:23 9990
-rw-rw-r-- 1 test test 0 11月 24 22:23 9991
-rw-rw-r-- 1 test test 0 11月 24 22:23 9992
-rw-rw-r-- 1 test test 0 11月 24 22:23 9993
-rw-rw-r-- 1 test test 0 11月 24 22:23 9994
-rw-rw-r-- 1 test test 0 11月 24 22:23 9995
-rw-rw-r-- 1 test test 0 11月 24 22:23 9996
-rw-rw-r-- 1 test test 0 11月 24 22:23 9997
-rw-rw-r-- 1 test test 0 11月 24 22:23 9998
-rw-rw-r-- 1 test test 0 11月 24 22:23 9999
real 0m0.678s
user 0m0.092s
sys 0m0.196s
real(実際に掛かった時間)が0.678秒、それに対しuser(プログラム=Bashが計算を行っていた時間)が0.092秒、sys(OSが処理をした時間)が0.196秒
すなわち、殆どがディスクI/O、他にOSの処理が多くを占めている事が見て取れます。
シンボリックリンク
次に、シンボリックリンクに対して同じ事をやってみましょう。
~$ time ls -l ./b
~~~ 略 ~~~
lrwxrwxrwx 1 test test 17 11月 24 22:42 9990 -> /home/test/a/9990
lrwxrwxrwx 1 test test 17 11月 24 22:42 9991 -> /home/test/a/9991
lrwxrwxrwx 1 test test 17 11月 24 22:42 9992 -> /home/test/a/9992
lrwxrwxrwx 1 test test 17 11月 24 22:42 9993 -> /home/test/a/9993
lrwxrwxrwx 1 test test 17 11月 24 22:42 9994 -> /home/test/a/9994
lrwxrwxrwx 1 test test 17 11月 24 22:42 9995 -> /home/test/a/9995
lrwxrwxrwx 1 test test 17 11月 24 22:42 9996 -> /home/test/a/9996
lrwxrwxrwx 1 test test 17 11月 24 22:42 9997 -> /home/test/a/9997
lrwxrwxrwx 1 test test 17 11月 24 22:42 9998 -> /home/test/a/9998
lrwxrwxrwx 1 test test 17 11月 24 22:42 9999 -> /home/test/a/9999
real 0m0.935s
user 0m0.096s
sys 0m0.272s
realが0.935秒で先程の0.678秒に対して微妙に時間が掛かっています。
userは0.096秒で先程の0.092秒から殆ど変わっていません。
sysが0.272秒で先程の0.196秒に比べて少し増加しています。
多少ながらオーバーヘッドはあるみたいですが、それらは殆ど無視できると言っていいレベルでしょう。
とは言えこれでは中身が薄すぎて記事的に問題があります、書き込み、読み込みの面でも見てみましょう。
測定(書き込み編)
測定に使うコマンドは以下の通り
time echo -n "1" | tee ./a/{1..9999}
ちなみにファイルのリセット(空にする)には以下を
time echo -n "" | tee ./a/{1..9999}
実ファイル
さぁ、そくて……
tee: ./a/9995: Too many open files
tee: ./a/9996: Too many open files
tee: ./a/9997: Too many open files
tee: ./a/9998: Too many open files
tee: ./a/9999: Too many open files
おや、予想外の事態です、ファイルディスクリプタの数を爆上げします。
ulimit -n 65536
ファイルをリセットするついでにToo manyが出ないのを確認。
気を取り直して、測定!!
time echo -n "1" | tee ./a/{1..9999}
1
real 0m1.987s
user 0m0.596s
sys 0m1.192s
やはり殆どはI/O処理のようです。
ただ、先ほどに比べてOSの処理で占める割合が大幅に増えました。
シンボリックリンク
シンボリックリンクに対しても同じ操作を行いましょう。
time echo -n "1" | tee ./b/{1..9999}
1
real 0m2.220s
user 0m0.592s
sys 0m1.336s
どうやらやはり多少のオーバーヘッドがあるみたいです。
ただ、それらも運用の際には無視できるレベルでしょう。
測定(読み込み編)
なんとなく結果が予想出来てしまいますが、読み込みの方ではどうでしょうか。
先程「1」を書き込んだ全ファイルに対して以下のコマンドを実行しましょう。
time cat ./a/{1..9999}
予想よりも実測、さっそく試してみましょう。
実ファイル
いざ、測定!!
time cat ./a/{1..9999}
~~~ 略 ~~~
real 0m4.506s
user 0m0.044s
sys 0m1.284s
キャッシュを消すと目に見えて遅くなるのが面白いですね。
シンボリックリンク
なんとなく予想出来てしまいますが、ささっと測定。
time cat ./a/{1..9999}
~~~ 略 ~~~
real 0m4.598s
user 0m0.032s
sys 0m1.428s
やはり、ほんの少し、ほんの少しだけオーバーヘッドがあるみたいです。
結果
シンボリックリンクにはほんの少しだけオーバーヘッドはあるみたいです。(もちろんOSやファイルシステムの違いで多少の違いはあるでしょうが……)
ただ、それらは運用の際には無視できるレベルでしょう、計測時の誤差の方が大きいくらいです。
そもそもこの「ほんの少しだけ」を気にしなくてはならない(くらいに高負荷)のであれば、さっさとスケールアウトしてマシンを増やすべきです、ええ、きっと。
かと言ってシンボリックリンクの乱用も考え物です、PHPだとホットデプロイの際になんだか問題があるみたいです。
参考:PHPにおけるシンボリックリンクを使ったデプロイの危険性について(「realpath_cache」和訳) : KLabGames Tech Blog
オマケ:ハードリンク
ハードリンクは同じinodeを参照するようにしただけなのでオーバーヘッドはないと予想、念のため確認しておきましょうか。
ls
以下が実ファイル
time ls -l ./a
~~~ 略 ~~~
-rw-rw-r-- 2 test test 0 11月 24 23:56 9990
-rw-rw-r-- 2 test test 0 11月 24 23:56 9991
-rw-rw-r-- 2 test test 0 11月 24 23:56 9992
-rw-rw-r-- 2 test test 0 11月 24 23:56 9993
-rw-rw-r-- 2 test test 0 11月 24 23:56 9994
-rw-rw-r-- 2 test test 0 11月 24 23:56 9995
-rw-rw-r-- 2 test test 0 11月 24 23:56 9996
-rw-rw-r-- 2 test test 0 11月 24 23:56 9997
-rw-rw-r-- 2 test test 0 11月 24 23:56 9998
-rw-rw-r-- 2 test test 0 11月 24 23:56 9999
real 0m0.708s
user 0m0.088s
sys 0m0.212s
以下がハードリンク
time ls -l ./a
~~~ 略 ~~~
-rw-rw-r-- 2 test test 0 11月 24 23:56 9990
-rw-rw-r-- 2 test test 0 11月 24 23:56 9991
-rw-rw-r-- 2 test test 0 11月 24 23:56 9992
-rw-rw-r-- 2 test test 0 11月 24 23:56 9993
-rw-rw-r-- 2 test test 0 11月 24 23:56 9994
-rw-rw-r-- 2 test test 0 11月 24 23:56 9995
-rw-rw-r-- 2 test test 0 11月 24 23:56 9996
-rw-rw-r-- 2 test test 0 11月 24 23:56 9997
-rw-rw-r-- 2 test test 0 11月 24 23:56 9998
-rw-rw-r-- 2 test test 0 11月 24 23:56 9999
real 0m0.664s
user 0m0.144s
sys 0m0.160s
オーバーヘッドみたいな物はなさそうですね、シンボリックリンクとハードリンクの違いを考えたら当たり前ですが……
一応読み書きの方でも確認しましょう。
書き込み
以下が実ファイル
time echo -n "1" | tee ./a/{1..9999}
1
real 0m1.097s
user 0m0.492s
sys 0m0.392s
以下がハードリンク
time echo -n "1" | tee ./b/{1..9999}
1
real 0m1.009s
user 0m0.448s
sys 0m0.380s
読み込み
以下が実ファイル
time cat ./a/{1..9999}
~~~ 略 ~~~
real 0m4.413s
user 0m0.052s
sys 0m1.248s
以下がハードリンク
time cat ./b/{1..9999}
real 0m4.777s
user 0m0.064s
sys 0m1.400s
ええ、オーバーヘッドなんてなさそうですね。
結論:シンボリックリンクは無視できるレベルのオーバーヘッドあり、ハードリンクはオーバーヘッドなし