読者です 読者をやめる 読者になる 読者になる

もっふもっふにしてやんよ!

勉強会行った話9割、手を動かした話1割。

0埋めファイルを作るとき、ddよりもheadが早い!?そんな馬鹿な!?

いきさつ

キッカケは、以下のツイートを拝見したからです。


想定と実行時の違い

脳内イメージでは以下のような感じで、bs=1GBで1回実行のほうが早いんじゃね?って思いました。

f:id:moffumoffu:20170402202113j:plain

しかし、実際にやってみたところ、結果が異なっていました。

そんなわけで、認識とはズレていたので、結局何が早いんだろうというところから、付随して原因までわかったので書きます。

実行環境

  • VMware Workstation 12 Player
  • CentOS 7.2 minimal
    • 割り当てメモリ: 8GiB
    • 割り当てHDD:40GB(事前割り当て)
    • ゲストOS内HDDフォーマット:ext4

結論

大して変わらん!

  • headとddで、0埋めファイルを生成する早さは変わらない
    • 早さに違いがあったのは、ディスクへの書き込みの実行タイミングが、ファイルディスクリプタの変更によってOSに任されたため
      • headでは、OSに任される
      • ddでは、アプリケーションで書き込み完了まで行う
      • OSのI/O処理に依存させないため、時間計測にsyncコマンドを追加した
    • 実体のファイルを作成するので安心
  • 同様の目的を果たすのに、truncateとfallocateコマンドが存在する
    • ファイルの実体は作成されないので、アプリケーションごとにメモリ割り当ての際不都合が発生する場合があるので、オススメできない。
    • 上記2つのコマンドでは、スパースファイルを作成する
    • ファイルを読み出す関数であるread()が呼ばれたとき、仕様として0を返します
    • メモリに割り当てる関数であるmmap()が実行された場合は正直よくわかってません。。。

よく考えたら・・・

リクエストのことすっかり忘れてた・・・

結局、パフォーマンスは変わらないので、/dev/zeroから持ってくる方法であれば、ddでもheadでもいいと思います。(truncateとfallocateは微妙にやりたいことと違う点、また、他にファイル生成の案が出なかったため。)

付記(headとddの違いについて)

  • bsによるメモリサイズの調整は、少なくとも1GB程度のテストファイルでは有意な差は見られませんでした
    • head、dd共に内部では、read関数やwrite関数を呼び出す
      • headでは8KBもしくは4KB単位での操作(bs=8KB,4KBと同等のシステム関数呼び出し)
      • 何万回と呼び出されていても速度に変わりなかった
    • TBなどのスケールだと、チューニングの効果が出るかもしれません

ソースコード

検証に使用したシェルスクリプトを記載しています。汚くてゴメンナサイ。

#!/bin/bash
rm *file -f;

function syori(){
  free -m > /dev/null
  echo 3 > /proc/sys/vm/drop_caches
  free -m > /dev/null
}

#head
echo "------------head---------------"
syori
time bash -c 'head -c 1GiB /dev/zero > headfile;sync;sync;sync'

#dd bs=4MB
echo "------------dd bs=4MiB---------"
syori
time bash -c 'dd if=/dev/zero of=4MBfile bs=4MiB count=256;sync;sync;sync'

#dd bs=1GiB
syori
echo "------------dd bs=1GiB---------"
time bash -c 'dd if=/dev/zero of=1GBfile bs=1GiB count=1;sync;sync;sync'

#truncate
syori
echo "------------truncate-----------"
time bash -c 'truncate -s 1G truncatefile;sync;sync;sync'

#fallocate
syori
echo "------------fallocatte---------"
time bash -c 'fallocate -l 1GiB fallocatefile;sync;sync;sync'


echo "------------syuuryou-----------"
syori

実行ログ(一例)

実行結果は、以下の通りです。time関数により計測しています。
十数回ほど実行しましたが、実行結果毎に1秒以上の差は出ませんでした。

[root@localhost tmp]# ./command.sh 
------------head---------------

real    0m5.736s
user    0m0.024s
sys     0m0.465s
------------dd bs=4MiB---------
256+0 レコード入力
256+0 レコード出力
1073741824 バイト (1.1 GB) コピーされました、 0.872527 秒、 1.2 GB/秒

real    0m6.139s
user    0m0.002s
sys     0m0.425s
------------dd bs=1GiB---------
1+0 レコード入力
1+0 レコード出力
1073741824 バイト (1.1 GB) コピーされました、 0.653189 秒、 1.6 GB/秒

real    0m5.760s
user    0m0.002s
sys     0m0.550s
------------truncate-----------

real    0m0.011s
user    0m0.000s
sys     0m0.007s
------------fallocatte---------

real    0m0.013s
user    0m0.000s
sys     0m0.007s
------------syuuryou-----------

謝辞

今回の調査に際して、以下の放送でプログラミング生放送コミュニティを利用させていただきました。コミュニティ運営の皆様、そしてコメントをしていただいた皆様、さらに視聴いただいた皆様、ありがとうございました。

live.nicovideo.jp