A Day In The Boy's Life -2ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

アプリケーション規模が大きくなってくるとボトルネックの1つとしてディスクの性能とかが気になってきたりして、どのくらいのパフォーマンスが出せるものかテストをしたくなってきたりもします。

で、調べてみるとfioというコマンドがあってFlexible I/O Testerの略らしいんですけど、テスト対象のディスク性能を数値化してくれたりします。

ここでの実行環境はRedHat6をベースにしてます。

 

 

fioの使い方

 

各ディストリビューションのパッケージとしても用意されているようですが、ここではソースからのインストール方法を。

ソースは、GitHub上から適当なバージョンをダウンロードします。

後はお決まりのconfigureからのmake installとなるのですが、その前にfioのディスク書き込みテストを非同期で行いたい場合はlibaioというライブラリが必要になってきます。

そこで、このパッケージを事前にインストールしておきます。

 

# yum install libaio-devel.x86_64

 

インストール完了後はfioのインストール。

 

$ ./configure

$ make

# make install

 

fioコマンドの使い方は非常にオプションが多いのですが、

 

# fio --ioengine=libaio --iodepth=16 --direct=1 --size=100m --numjobs=16 --group_reporting --directory=/tmp --bs=4k --output=/root/result.txt --name=random-r70w30 --rw=randrw --rwmixread=70

 

のように使います。 ioengineオプションはlibaioで先ほどのライブラリを指定していますが、IOをどういう風に発生させるかを決めるおオプションです。

同期書き込み(デフォルトのsync)や非同期のlibaioなどを指定します。 使用できるioengineは下記のヘルプで確認できます。

 

# fio --enghelp                                                                                                                                                         
Available IO engines:
        binject
        sg
        splice
        e4defrag
        falloc
        posixaio
        libaio
        filecreate
        ftruncate
        net
        netsplice
        null
        sync
        psync
        vsync
        pvsync
        pvsync2
        mmap
        cpuio

 

次に、iodepthは同時書き込みをどれくらいの数で発生させるかというオプションで、numjobsも同じ数にしていますがこちらも同時実行スレッド数を指定するものなのですがあんまり違いが判ってません・・・。

sizeはテスト用ファイルのサイズ、directoryは書き込み先のディレクトリ、bsはブロックサイズです。

rwオプションはテストの読み書き方法でreadを指定すれば読み込みのみを、randwriteを指定すればランダムの書き込みを、上記のよう見randrwを指定すればランダムな読み書きを実行します。

併せてrwmixreadオプションは読み書きを実行する場合にどれくらいの割合にするかを指定するもので、この場合は70%がread(つまり読み込み)、残りが書き込みテストとなります。

書き込みを多くテストしたい場合はこの割合を調整すればよいです(逆の意味を指定するrwmixwriteというオプションもありますが)。 

これ以外にもオプションが豊富なので、その他のオプションを確認したい場合は、他の解説しているサイトなどをご参照ください・・・。 

 

fioコマンド @ 新宿区で働くプロジェクトマネージャーのブログ

 

で、実行結果。

 

random-r70w30: (g=0): rw=randrw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=16
...
fio-3.2
Starting 16 processes
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)
random-r70w30: Laying out IO file (1 file / 100MiB)

random-r70w30: (groupid=0, jobs=16): err= 0: pid=2394: Sat Dec 19 22:17:11 2017
   read: IOPS=2148, BW=8593KiB/s (8799kB/s)(1119MiB/133291msec)
    slat (usec): min=4, max=274614, avg=3149.74, stdev=13009.06
    clat (usec): min=122, max=643285, avg=82131.10, stdev=32362.04
     lat (usec): min=405, max=643293, avg=85282.41, stdev=34297.45
    clat percentiles (msec):
     |  1.00th=[   17],  5.00th=[   34], 10.00th=[   50], 20.00th=[   62],
     | 30.00th=[   69], 40.00th=[   75], 50.00th=[   81], 60.00th=[   86],
     | 70.00th=[   91], 80.00th=[   99], 90.00th=[  113], 95.00th=[  138],
     | 99.00th=[  199], 99.50th=[  224], 99.90th=[  288], 99.95th=[  351],
     | 99.99th=[  464]
   bw (  KiB/s): min=   56, max= 1898, per=6.27%, avg=538.82, stdev=140.85, samples=4242
   iops        : min=   14, max=  474, avg=134.40, stdev=35.18, samples=4242
  write: IOPS=924, BW=3699KiB/s (3788kB/s)(481MiB/133291msec)
    slat (usec): min=6, max=242011, avg=3140.93, stdev=12863.42
    clat (usec): min=182, max=643278, avg=74573.84, stdev=32149.02
     lat (usec): min=260, max=643287, avg=77716.39, stdev=31591.33
    clat percentiles (msec):
     |  1.00th=[   15],  5.00th=[   23], 10.00th=[   32], 20.00th=[   52],
     | 30.00th=[   63], 40.00th=[   70], 50.00th=[   77], 60.00th=[   82],
     | 70.00th=[   87], 80.00th=[   94], 90.00th=[  106], 95.00th=[  124],
     | 99.00th=[  176], 99.50th=[  207], 99.90th=[  249], 99.95th=[  296],
     | 99.99th=[  502]
   bw (  KiB/s): min=   32, max=  844, per=6.27%, avg=231.76, stdev=68.16, samples=4242
   iops        : min=    8, max=  211, avg=57.82, stdev=17.00, samples=4242
  lat (usec)   : 250=0.01%, 500=0.01%, 750=0.02%, 1000=0.01%
  lat (msec)   : 2=0.02%, 4=0.03%, 10=0.11%, 20=2.71%, 50=10.02%
  lat (msec)   : 100=70.29%, 250=16.61%, 500=0.16%, 750=0.01%
  cpu          : usr=0.15%, sys=0.28%, ctx=94053, majf=0, minf=544
  IO depths    : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=99.9%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.1%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued rwt: total=286337,123263,0, short=0,0,0, dropped=0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=16

Run status group 0 (all jobs):
   READ: bw=8593KiB/s (8799kB/s), 8593KiB/s-8593KiB/s (8799kB/s-8799kB/s), io=1119MiB (1173MB), run=133291-133291msec
  WRITE: bw=3699KiB/s (3788kB/s), 3699KiB/s-3699KiB/s (3788kB/s-3788kB/s), io=481MiB (505MB), run=133291-133291msec

Disk stats (read/write):
  sdb: ios=282335/122545, merge=3682/840, ticks=14128940/4417402, in_queue=18548219, util=100.00%

 

色々出てくるんですけど、分かりやすく指標にするとしたらIOPSの性能部分ではないでしょうか。

Input Output Per Secondの略らしいですけど、1秒間に読み書きできる回数を数値化したものです。

切り出してみると下記のようになっています。 

 

read: IOPS=2148, BW=8593KiB/s (8799kB/s)(1119MiB/133291msec)
write: IOPS=924, BW=3699KiB/s (3788kB/s)(481MiB/133291msec)

 

後述するI/O関連のオプションをチューニングしてみてfioでテストしIOPSの性能値を比較してみるということで改善したかどうかがわかってきそうです。

 

 

I/Oスケジューラをチューニングする

 

I/Oスケジューラとは簡単に言うとディスクへのI/O要求が発生した際にどういうアルゴリズムでその処理を行うかを決めるもので、cfqやnoop、deadlineといったアルゴリズムが存在します。

現在採用しているI/Oスケジューラは下記のコマンドで確認できます。

 

# cat /sys/block/sdb/queue/scheduler 
noop anticipatory deadline [cfq]

 

最近のLinuxではcfqがデフォルトになっていることが多いのですが、cfqはキューにため込んだI/O要求自体のスケジュールを平均になるように処理するようで通常はあんまり気にならなかったりもするんですけど、I/Oが多くなってくるとその辺の処理のオーバーヘッドが顕著になってくる場合もあります。

(多分、ディスクへの書き込みがかなりランダムなのか、それともログのようなシーケンシャルなものが多いのかなどによって変わってくるんだと思いますが)

で、このI/Oスケジューラを変更してみたいと思います。

単純に変更する場合は、

 

# echo noop > /sys/block/sdb/queue/scheduler
# cat /sys/block/sdb/queue/scheduler 
[noop] anticipatory deadline cfq 

 

のようにして変更できます。

上記の場合は、noopに変更しています。

noopは難しいアルゴリズムなしに単純に要求順にI/Oを処理していくものというものです。

この状態で再度fioコマンドでIOPS値を計測してみます。

 

[cfq]
   read: IOPS=583, BW=2336KiB/s (2392kB/s)(1119MiB/490331msec)
  write: IOPS=251, BW=1006KiB/s (1030kB/s)(481MiB/490331msec)

[noop]
   read: IOPS=3288, BW=12.8MiB/s (13.5MB/s)(1119MiB/87073msec)
  write: IOPS=1415, BW=5663KiB/s (5798kB/s)(481MiB/87073msec)

 

noopにすることでかなりパフォーマンスが改善しました。

ただ、これは環境によるので一概にnoopにすればいいというわけではありません。

また、場合によってはdeadlineなどほかのI/Oスケジューラにした方が改善されるかもしれません。

ということで、環境ごとにI/Oスケジューラの変更とfioを使ったパフォーマンスの計測を繰り返してチューニングしてみるのが良いかと思います。

ちなみに、上記のI/Oスケジューラの変更ではOSを再起動すると元に戻ります。

ですので、永続的にI/Oスケジューラを変更する場合はgrub.confなど起動時にオプションに、下記のようにelevatorオプションにてI/Oスケジューラを指定しておきます。

 

kernel /vmlinuz-2.6.32-573.3.1.el6.x86_64 ro root=UUID=2a16f496-9e5a-4312-815a-4c64c5b1fcd4 rd_NO_LUKS rd_NO_MD  KEYBOARDTYPE=pc KEYTABLE=jp106 LANG=ja_JP.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet crashkernel=auto elevator=noop

 

パフォーマンスというとCPUやメモリの使用率などに目を向けがちですが、こういったディスク性能にも大きく引っ張られることもあるので確認やチューニングしてみるのもよいかもしれません。

 

 

 

一昔前の場合、ある技術を身につけるのにはそれなりの時間がかかっていました。

それは、体系的に整えられたマニュアルが存在しなかったり、ネットもなかったり(または情報が著しく少なかったり)、実際にその現場でその人を見て吸収しなければならなかったりと制約は様々だったんでしょうけど、今やそんなことは遠い昔となっています。

寿司屋の修行で「飯炊き3年握り8年」という言葉あるそうで(そして、それについて数年前に論争されてましたけど)、ある技術を身につけるだけなら実際今でもCookpadでレシピみたり、投稿された料理動画をみれば一定レベルのところまではスキップできると思います。

 

ただ、技術だけを身につけるならそれでよくても、実際それだけでは役に立たないことも多く、結局のところ複合的な技術力の集大成で個人としてのスキルを表現できたり、技術以外のところを占める領域というのも社会を生き抜くためには少なくありません。

 

 

経験を積んだ人を採用するのが是か非か

 

一般的に誰かを採用する際に持ち込まれるのは経歴書であり、そこには細かくその人の業務経験が書かれています。

そして、一般的にそこに書かれていることの数が多く、そして業務年数が長い方が「業務経験が豊富だから即戦力になるのではないか」とか「色んな現場で活躍してきたわけだから応用力や順応力があるのでは」などの理由で印象はよくなります。

まぁ、もちろん採用の中での質問事項で粗探しが始まるのでその効力も書類選考の段階までというのはあるものの、経験年数というのは数値化できるという点において良くも悪くも定量的に判断できる基準と捉えられがちです。

 

一方で、先に書いたように現代においてはその業務経験年数というのが必ずしもスキルレベルにマッチするというわけではないのですが、それを定量的に表現する方法もあまりありません。

1年で猛勉強してプログラミングをマスターしたとしても10年業務経験がある人と比べたら評価されにくい状況になりがちです。

エンジニアとしてならソースコードを見てもらうとか、構築したサービスを見てもらうのが手っ取り早いわけですが、それが難しい分野も多々あるでしょう。

これはその人の表現力などに依存するのもあって、ブログやSNSなどを通して活発に発言していく中で周りに伝播していって、アクセス数やフォロワーの数などといった定量的に見えるてくる場合もあります(もちろんそれが全てではないですけど)

 

つまりは、自分がどのレベルのスキルを持っているのかというのを業務経験年数以外に表現する術というものも必要になってきますし、逆に技術を習得する時間が短くて済む時代であるからこそその人の本当の能力を見抜く術というのも大事になってきているんだと思うわけです。

 

 

長い業務経験を経て身につくこと

 

例えば、10年という業務経験において身につくものというのは会社から支給される業務をこなしていった結果であって、そこには必要ない無駄な仕事というのも多分に含まれたりします。

本来自分が磨き上げたい技術とは異なる分野の仕事をする羽目になるかもしれませんし、1日の業務の内訳を見ていても面倒な事務処理に関する時間が多くを占めているケースもあります。

 

大学で最先端の技術を研究している教授とかでも実際にその研究に使える時間というのはほんのわずかで多くを事務処理や他の研修生の教育などに時間を割かれているというニュースをテレビで見たことがありますが、一般的な企業の中でも優秀な人に対しその人が絶対的に能力を発揮できる現場以外に回したり、時間を食いつぶしているケースはよく見かけます

ですので、業務経験10年といっても実際のところその技術を身につけるだけに要する時間というのは1年分ぐらいなのかもしれません。

10000時間の法則というのもあるぐらいですしね。

 

ただ、裏を返せばその10000時間という経験値を得るためには実際にそれよりもずっと長い業務時間が必要だという現実もあったりします。

そんな無駄なことばかりやらせる会社なんて辞めてしまえ、というのはあるものの10000時間を丸々自分のスキルを身につけるためだけに時間を与えてくれる現場なんてなかなかないものです。

そして、それは会社の中だけの話でなくプライベートで起こる出来事に大きく左右されたりもします。

 

もう1つ言えるのは技術をマスターするのに10000時間かかるとしても、1000時間を費やしたあたりで「俺、大体のことわかったんじゃね?」的な理解した感が出てきたりもします。

企業が主催する有料研修とかの日程を見ても長くて5日程度で組まれていたりするので、1か月以上の時間を費やせば学ぶことのでき範囲というのは相当広がるでしょう。

ただ、これは結構危険な感覚でもあってその分かった感によってその技術を学ぶのを止めてしまう人も多かったりします。

1000という時間を使って技術を使いこなせるようになっても、それを正しく理解するのにはさらに相当な学習経験を費やさないといけないわけですが、そういった領域というのは最初の1000時間の中では出くわすケースも少なく問題にならないことがよくあります。

ですので残りの長い時間の経験そのものが技術を理解する期間としては大事になってくるんじゃないかと思うわけです。

実際の業務経験というのはその技術を習得した後に、どれくらいの間それを継続できたのかということなのかもしれません。

 

 

 

 

 

これはよくわかるし実際そうなっちゃうことも多い。

ただ、実際そうなってしまう理由を考えてみると状況や環境の制約などによるものも多いと思ったり。

 

 

細切れの時間の中でできること

 

1つは、本来やらないといけない優先度の高いタスクってある程度大きな時間の枠が必要なものが多かったりするわけなんですけど、それを押し込める時間が1日の中にあまりないという点。

会社でのスケジュールを見ていると、打ち合わせが飛び飛びで入っているし、決められた時間で作業しないといけないタスクもあるし、時間が空いたと思ったら「今いいですか?」とか声かけられるし、やらないといけないタスクを集中して実行する時間枠が取れなかったりするわけです。

エンジニア視点で言うと、コードを書くときには集中できる時間を確保したいと思いますし、バグをつぶしていたり調査しているときは誰も声をかけるなオーラを出そうとしたりして、なるべくそれに注力できる時間を確保したいと思うものの実際には他の要因に押しつぶされてしまうのが現状だったりもします。

 

もちろんこれはまとめた時間でやる必要がないわけなんですけど、やっぱり中途半端なところで打ち合わせに行って戻ってきて再開するのって気持ちの面でも持ち直すのに時間がかかったり、再開するときにどこまでやってたのか整理するのに時間がかかったりと非効率であったりもします。

ただ、それはタスクの細分化が下手なだけで、限られた時間の中で達成すべきタスクをうまく細切れにできるかということなんだと思います。

だから優先度が高くとも低くとも、その粒度が大きくとも小さくとも、うまくタスクを平準化するのがコツになってきます。

 

もう1点は、体調とかによって自分の中で制限をかけてしまうという点。

風邪気味だから今日は簡単なタスクだけにしようとか、二日酔いで頭痛くてこんな状態で重要なタスクこなしたら事故るから今日は事務処理の日にしようとか。

まぁこれは自己責任ではあるんですけど、体調によってものすごく集中して短期間でタスクをこなしていける日と、なんか気が乗らなくて集中できない日というのは誰しもあるのではないでしょうか。

 

優先度の低い細かいタスクとは言え、やらないといけないことには変わりはないので、集中する時間が取れなかったり気が乗らないというときはその細かいタスクを徹底的に潰してしまって、明日の集中できる時間を確保する取り組みに変えることも大事なのではないかと思ったりします。

 

 

優先度の低いタスクを隠す取り組み

 

こういうことは自分自身だけでなく、他者へ仕事をお願いするときにも同様に発生します。

優先度の高いものをやってほしいのに、低いものだけやたら頑張ってこなしている人を見ると「それじゃないのになー」とか思ったりするわけです。

若手のエンジニアにぼそっと不具合の事や機能の改善の話をすると、お願いしていることそっちのけで調査を始められたりすることがあります。

それはそれでありがたいことなんですけど、優先順位的には高くないし納期が迫っている別のタスクがあるならその分そっちに集中してほしいと思ったりします。

 

ですので、優先順位の低いタスクをお願いする場合は、各メンバーの稼働の状況を見て忙しそうなら手元にしまったままにし、余裕が出てきたタイミングでお願いするようなことをしたりします。

タスクを可視化してどういう優先度になっているのかを明示してからお願いする方が互いに仕事の順番を見誤らなくて済んだりもするんですけど、タスクを可視化するのは自分が抱えている業務以外にどんなタスクがあるのかという興味が生まれて集中力を分散してしまう結果にもなりかねないので諸刃の剣でもあると思ったりはします。

要は他が気になりだして本業がおろそかになってきたり、技術的な要素とかが含まれると「それ面白そう」という気持ちが生まれてきていつの間にかそればっかりやっているとか。

 

こういうのって経験を問わず陥る罠だったりもするので、優先順位は低いものの他にやらないといけないことがあるというその存在自体を隠してしまう方が集中して目の前の業務をこなしたりできるのではないかと思います。

優先順位が低いので最悪遅延が出てもそこまで支障はないでしょうし、細かいからそれほど時間もかからないものだったりもしますし、小出しにしてやるべき順番をうまくコントロールするのは特に若手メンバーなどでは有効な手段ではないかと思ったりします。