いきさつ
キッカケは、以下のツイートを拝見したからです。
「head -c 100MB /dev/zero >image.iso」目から鱗。ddと比べてどっちのほうが早いだろう。 / “The Cult of DD” https://t.co/hTjkGCovHn
— matsuu Period. (@matsuu) 2017年4月2日
想定と実行時の違い
脳内イメージでは以下のような感じで、bs=1GBで1回実行のほうが早いんじゃね?って思いました。
しかし、実際にやってみたところ、結果が異なっていました。
そんなわけで、認識とはズレていたので、結局何が早いんだろうというところから、付随して原因までわかったので書きます。
実行環境
結論
大して変わらん!
- headとddで、0埋めファイルを生成する早さは変わらない
- 早さに違いがあったのは、ディスクへの書き込みの実行タイミングが、ファイルディスクリプタの変更によってOSに任されたため
- headでは、OSに任される
- ddでは、アプリケーションで書き込み完了まで行う
- OSのI/O処理に依存させないため、時間計測にsyncコマンドを追加した
- 実体のファイルを作成するので安心
- 早さに違いがあったのは、ディスクへの書き込みの実行タイミングが、ファイルディスクリプタの変更によってOSに任されたため
- 同様の目的を果たすのに、truncateとfallocateコマンドが存在する
- ファイルの実体は作成されないので、アプリケーションごとにメモリ割り当ての際不都合が発生する場合があるので、オススメできない。
- 上記2つのコマンドでは、スパースファイルを作成する
- 詳しくは スパースファイル - ArchWiki を参照のこと。
- ファイルを読み出す関数であるread()が呼ばれたとき、仕様として0を返します
- メモリに割り当てる関数であるmmap()が実行された場合は正直よくわかってません。。。
よく考えたら・・・
リクエストのことすっかり忘れてた・・・
一番パフォーマンスが出るところを調べて欲しくはある。 https://t.co/ISlL0Mi4Q0
— Takashi Kawasaki (@espresso3389) 2017年4月2日
結局、パフォーマンスは変わらないので、/dev/zeroから持ってくる方法であれば、ddでもheadでもいいと思います。(truncateとfallocateは微妙にやりたいことと違う点、また、他にファイル生成の案が出なかったため。)
付記(headとddの違いについて)
- bsによるメモリサイズの調整は、少なくとも1GB程度のテストファイルでは有意な差は見られませんでした
- head、dd共に内部では、read関数やwrite関数を呼び出す
- headでは8KBもしくは4KB単位での操作(bs=8KB,4KBと同等のシステム関数呼び出し)
- 何万回と呼び出されていても速度に変わりなかった
- TBなどのスケールだと、チューニングの効果が出るかもしれません
- head、dd共に内部では、read関数やwrite関数を呼び出す
ソースコード
検証に使用したシェルスクリプトを記載しています。汚くてゴメンナサイ。
#!/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-----------
謝辞
今回の調査に際して、以下の放送でプログラミング生放送コミュニティを利用させていただきました。コミュニティ運営の皆様、そしてコメントをしていただいた皆様、さらに視聴いただいた皆様、ありがとうございました。