1 TB=ファイルがあります。バイト12345678901からバイト19876543212までを読み取り、それを100 MB RAMのマシンの標準出力に置きたいです。
これを行うPerlスクリプトを簡単に作成できます。 sysreadは700 MB /秒(これは問題ありません)を提供しますが、syswriteは30 MB /秒しか提供しません。より効率的なもの、できればすべてのUnixシステムにインストールされ、1 GB /秒のオーダーで配信できるものが欲しいです。
私の最初のアイデアは:
dd if=1tb skip=12345678901 bs=1 count=$((19876543212-12345678901))
しかし、それは効率的ではありません。
編集:
Syswriteを間違って測定した方法がわかりません。これにより、3.5 GB /秒が実現します。
Perl -e 'sysseek(STDIN,shift,0) || die; $left = shift; \
while($read = sysread(STDIN,$buf, ($left > 32768 ? 32768 : $left))){ \
$left -= $read; syswrite(STDOUT,$buf);
}' 12345678901 $((19876543212-12345678901)) < bigfile
yes | dd bs=1024k count=10 | wc
悪夢。
ブロックサイズが小さいため、これは低速です。最近のGNU dd
( coreutils v8.16 + ))を使用する最も簡単な方法は、skip_bytes
およびcount_bytes
オプションを使用することです。
in_file=1tb
start=12345678901
end=19876543212
block_size=4096
copy_size=$(( $end - $start ))
dd if="$in_file" iflag=skip_bytes,count_bytes,fullblock bs="$block_size" \
skip="$start" count="$copy_size"
fullblock
オプションを上記の @ Gilles answer に従って追加しました。最初はcount_bytes
によって暗示されるのではないかと思ったのですが、そうではありません。
上記の問題は、以下の潜在的な問題です。dd
s読み取り/書き込み呼び出しが何らかの理由で中断された場合、データが失われます。ほとんどの場合、これは起こりそうにありません(パイプではなくファイルから読み取るため、オッズはいくらか減少します)。
skip_bytes
およびcount_bytes
オプションなしでdd
を使用することは、より困難です。
in_file=1tb
start=12345678901
end=19876543212
block_size=4096
copy_full_size=$(( $end - $start ))
copy1_size=$(( $block_size - ($start % $block_size) ))
copy2_start=$(( $start + $copy1_size ))
copy2_skip=$(( $copy2_start / $block_size ))
copy2_blocks=$(( ($end - $copy2_start) / $block_size ))
copy3_start=$(( ($copy2_skip + $copy2_blocks) * $block_size ))
copy3_size=$(( $end - $copy3_start ))
{
dd if="$in_file" bs=1 skip="$start" count="$copy1_size"
dd if="$in_file" bs="$block_size" skip="$copy2_skip" count="$copy2_blocks"
dd if="$in_file" bs=1 skip="$copy3_start" count="$copy3_size"
}
異なるブロックサイズを試すこともできますが、その効果はそれほど劇的ではありません。参照- ddへのbsパラメータの最適値を決定する方法はありますか?
bs=1
は、dd
に一度に1バイトの読み取りと書き込みを行うように指示します。 read
とwrite
の呼び出しごとにオーバーヘッドが発生するため、これが遅くなります。適切なパフォーマンスを得るには、より大きなブロックサイズを使用します。
ファイル全体をコピーすると、少なくともLinuxでは、 cp
とcat
がdd
より高速であることがわかりました。大きなブロックサイズを指定します。
ファイルの一部のみをコピーするには、tail
をhead
にパイプします。これにはGNU coreutilsまたはhead -c
指定されたバイト数(tail -c
はPOSIXにありますが、head -c
はそうではありません)。 Linuxのクイックベンチマークは、これがdd
より遅いことを示しています。これはおそらくパイプが原因です。
tail -c $((2345678901+1)) | head -c $((19876543212-2345678901))
dd
の問題は、信頼できないことです。部分的なデータをコピーできます。私の知る限り、dd
は、通常のファイルの読み取りおよび書き込み時に安全です—参照 ddがデータのコピーに適しているのはいつですか?(または、read()とwrite()が部分的である場合)) —ただし シグナルによって中断されない限り 。 GNU coreutilsを使用すると、fullblock
フラグを使用できますが、これは移植性がありません。
dd
のもう1つの問題は、スキップされたバイト数と転送されたバイト数の両方がブロックサイズの倍数である必要があるため、機能するブロック数を見つけるのが難しいことです。 dd
への複数の呼び出しを使用できます。1つは最初の部分ブロックをコピーし、1つは整列したブロックの大部分をコピーし、もう1つは最後の部分ブロックをコピーします。詳細は Graeme's answer シェルスニペット。ただし、スクリプトを実行するときには、fullblock
フラグを使用していない限り、dd
がすべてのデータをコピーすることを祈る必要があることを忘れないでください。コピーが部分的である場合、dd
はゼロ以外のステータスを返すため、エラーを検出するのは簡単ですが、実際に修復する方法はありません。
POSIXは、シェルレベルで提供するより良いものはありません。私のアドバイスは、小さな特別な目的のCプログラムを書くことです(実装するものに応じて、dd_done_right
またはtail_head
またはmini-busybox
)。
dd
の場合:
dd if=1tb skip=12345678901 count=$((19876543212-12345678901)) bs=1M iflags=skip_bytes,count_bytes
または、losetup
を使用します。
losetup --find --show --offset 12345678901 --sizelimit $((19876543212-12345678901))
そしてdd
、cat
、...ループデバイス。
これはあなたがこれを行う方法です:
_ i=$(((t=19876543212)-(h=12345678901)))
{ dd count=0 skip=1 bs="$h"
dd count="$((i/(b=64*1024)-1))" bs="$b"
dd count=1 bs="$((i%b))"
} <infile >outfile
_
本当に必要なのはそれだけです。それ以上必要とするものではありません。そもそも_dd count=0 skip=1 bs=$block_size1
_は、通常のファイル入力に対して実質的に瞬時にlseek()
を実行します。 データの欠落、またはそれについて他の真実が伝えられている可能性はなく、目的の開始位置に直接シークできます。ファイル記述子はシェルによって所有されており、dd
はそれを継承しているだけなので、カーソルの位置に影響を与えるため、段階的に実行できます。とてもシンプルです-そして、dd
よりもタスクに適した標準的なツールはありません。
これは、多くの場合理想的な64kブロックサイズを使用します。一般的な考えに反して、ブロックサイズが大きいとdd
の動作が速くなりません。一方、小さなバッファーも良くありません。 dd
は、システムコールの時間を同期する必要があるため、データをメモリにコピーして再度コピーするのを待つ必要はありませんが、システムコールを待つ必要もありません。したがって、次のread()
が最後に待機する必要がないほど十分な時間をかけたいが、必要以上に大きなサイズでバッファリングしているほどではない。
したがって、最初のdd
は開始位置までスキップします。 zero 時間かかります。その時点で好きな他のプログラムを呼び出してそのstdinを読み取ると、目的のバイトオフセットで直接読み取りを開始できます。別のdd
を呼び出して、 _((interval / blocksize) -1)
_ countブロックをstdoutに読み取ります。
最後に必要なことは、前の除算演算の係数(存在する場合)をコピーすることです。そして、それはそれです。
ところで、人々が証拠なしで自分の顔に事実を述べているとき、それを信じてはいけません。はい、dd
が短い読み取りを実行することは可能です(そのようなことは、正常なブロックデバイスから読み取る場合は不可能です-したがって、名前)。このようなことは、ブロックデバイス以外から読み取られたdd
ストリームを正しくバッファリングしない場合にのみ可能です。例えば:
_cat data | dd bs="$num" ### incorrect
cat data | dd ibs="$PIPE_MAX" obs="$buf_size" ### correct
_
どちらの場合でも、dd
はデータの all をコピーします。最初のケースでは、(cat
)ではありそうもないことですが、dd
がコピーする出力ブロックの一部が「$ num "dd
が only で指定されているため、コマンドラインでバッファが明示的に要求されたときに、何でもバッファリングするため、バイト数。 _bs=
_は、最大ブロックサイズを表します。これは、dd
の purpose がリアルタイムI/Oであるためです。
2番目の例では、完全な書き込みができるまで、出力ブロックサイズとdd
バッファーの読み取りを明示的に指定しています。これは入力ブロックに基づく_count=
_には影響しませんが、そのためには別のdd
が必要です。他の方法で与えられた誤報は無視してください。