dd
繰り返し書き込むことができる\0
バイトファイルは非常に高速に書き込まれますが、繰り返しランダムな文字列を書き込むことはできません。
お持ちですか?バッシュシェル繰り返し書き込む方法ひも「dd」(含む)ほど速いですか\0
?
Linuxを使用しながら6ヶ月間私が遭遇したすべての提案は似ていますが、以下に示すものとprintf "%${1}s" | sed -e "s/ /${2}/g"
比較すると痛みを伴うほど遅く、約384MB(私の箱から)後にクラッシュが発生します。実際、これは一行で悪くありません。長さ:) - しかし、崩れます!文字列に改行文字が含まれていると問題にならないようです。dd
sed
sed
dd
printf
+との速度比較sed
:
real user sys
WRITE 384 MB: 'dd' 0m03.833s 0m00.004s 0m00.548s
WRITE 384 MB: 'printf+sed' 1m39.551s 1m34.754s 0m02.968s
# the two commands used
dd if=/dev/zero bs=1024 count=$((1024*384))
printf "%$((1024*1024*384))s" |sed -e "s/ /x/g"
これを行う方法についてのアイデアがあります。バッシュシェルスクリプトを作成しますが、ホイールを再作成する必要はありません。 :)
答え1
$ time perl -e \
'$count=1024*1024; while ($count>0) { print "x" x 384; $count--; }' > out
real 0m1.284s
user 0m0.316s
sys 0m0.961s
$ ls -lh out
-rw-r--r-- 1 me group 384M Apr 16 19:47 out
"x" x 384
(384を生成する文字列)x
を必要なものに置き換えます。
各ループでより大きな文字列を使用し、通常のstdoutバッファリングをバイパスすることで、これをさらに最適化できます。
$ perl -e \
'$count=384; while ($count>0) {
syswrite(STDOUT, "x" x (1024*1024), 1024*1024);
$count--;
}' > out
この場合、syswrite
コールはデフォルトのwrite
システムコールに1Mを渡すので、非常に良いです。 (これでユーザーあたり約0.940秒を得ました。)
sync
ヒント:前の実行のフラッシュが現在の実行のI / Oを妨げないように、各テスト間でこの呼び出しを呼び出す必要があります。
ちなみに今回は次のようになります。
$ time dd if=/dev/zero bs=1024 count=$((1024*384)) of=./out
393216+0 records in
393216+0 records out
402653184 bytes (403 MB) copied, 1.41404 s, 285 MB/s
real 0m1.480s
user 0m0.054s
sys 0m1.410s
答え2
一般に、シェルは大きなデータブロックを処理するのに遅いことが知られている。ほとんどのスクリプトでは、どのデータビットが小さい可能性があり、どのビットが大きい可能性があるかを事前に知ることができます。
- 外部プロセスをフォークして実行すると継続的なオーバーヘッドが発生するため、小さなデータを処理するには組み込みシェルを使用することをお勧めします。
- 専門化されたコンパイルツールは解釈された汎用言語よりも効率的であるため、ビッグデータ処理のために外部の専門ツールに頼ることを好みます。
dd
read
問題と通話にブロックサイズを使用してください。write
strace(またはOSによってはtruss、Trace...)を使用してこれを観察できます。
$ strace -s9 dd if=/dev/zero of=/dev/null ibs=1024k obs=2048k count=4
✄
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
write(1, "\0\0\0\0\0\0\0\0\0"..., 2097152) = 2097152
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
read(0, "\0\0\0\0\0\0\0\0\0"..., 1048576) = 1048576
write(1, "\0\0\0\0\0\0\0\0\0"..., 2097152) = 2097152
✄
他のほとんどのツールは、最大バッファサイズの上限がはるかに低いため、より多くのシステムコールを実行するため、時間がかかります。しかし、これは非現実的なベンチマークであることに注意してください。通常のファイル、パイプ、またはソケットに書き込むと、カーネルはシステム呼び出しごとに数キロバイトを超えるデータを書き込めない可能性があります。
答え3
これを使用してくださいdd
!まず、文字列がファイルの先頭に書き込まれます。次に、次のようにします。
dd if=$FILE of=$FILE bs=$STRING_LENGTH seek=1 count=$REPEAT_TIMES
注:$ STRING_LENGTHが小さい場合は、次のことができます。
dd if=$FILE of=$FILE bs=$STRING_LENGTH seek=1 count=$((1024/$REPEAT_TIMES))
dd if=$FILE of=$FILE bs=1024 seek=1 count=$(($REPEAT_TIMES/1024))
(この例は、STRING_LENGTHが2の累乗でREPEAT_TIMESが1024の倍数の場合にのみ機能しますが、理解できます。)
ファイルを上書きするために使用するには(消去など)、次を使用します。conv=notrunc
答え4
最後に、これを行う方法のアイデアを得ました...これは||近くで実行されているtee
チェーンを使用してください。tee
tee
dd
# ============================================================================
# repstr
#
# Brief:
# Make multiple (repeat) copies of a string.
# Option -e, --eval is used as in 'echo -e'
#
# Return:
# The resulting string is sent to stdout
#
# Args: Option $1 $2
# -e, --eval COUNT STRING
# repstr $((2**40)) "x" # 1 TB: xxxxxxxxx...
# eg. repstr -e 7 "AB\tC\n" # 7 lines: AB<TAB>C
# repstr 2 "ऑढळ|a" # 2 copies: ऑढळ|aऑढळ|a
#
[[ "$1" == "-e" || "$1" == "--eval" ]] && { e="-e"; shift 1; }|| e=""
count="$1"
string="$2"
[[ "${count}" == "" ]] && exit 1 # $count must be an integer
[[ "${count//[0-9]/}" != "" ]] && exit 2 # $count is not an integer
[[ "${count}" == "0" ]] && exit 0 # nothing to do
[[ "${string}" == "" ]] && exit 0 # nothing to do
#
# ========================================================================
# Find the highest 'power of 2' which, when calculated**, is <= count
# ie. check ascending 'powers of 2'
((leqXpo=0)) # Exponent which makes 2** <= count
((leqCnt=1)) # A count which is <= count
while ((count>=leqCnt)) ;do
((leqXpo+=1))
((leqCnt*=2))
done
((leqXpo-=1))
((leqCnt/=2))
#
# ======================================================================================
# Output $string to 'tee's which are daisy-chained in groups of descending 'powers of 2'
todo=$count
for ((xpo=leqXpo ;xpo>0 ;xpo--)) ;do
tchain=""
floor=$((2**xpo))
if ((todo>=(2**xpo))) ; then
for ((t=0 ;t<xpo ;t++)) ;do tchain="$tchain|tee -" ;done
eval echo -n $e \"'$string'\" $tchain # >/dev/null
((todo-=floor))
fi
done
if ((todo==1)) ;then
eval echo -n $e \"'$string'\" # >/dev/null
fi
#
時間テストの結果は次のとおりです。私が作成したいテストファイルのサイズと同じように32 GBまで増やしました(この問題を解決し始めました)。
NOTE: (2**30), etc. refers to the number of strings (to achieve a particular GB filesize)
-----
dd method (just for reference) real/user/sys
* 8GB =================================
if=/dev/zero bs=1024 count=$(((1024**2)*8)) # 2m46.941s / 00m3.828s / 0m56.864s
tee method: fewer tests, because it didn't overflow, and the number-of-strings:time ratio is linear
tee method: count string real/user/sys
* 8GB ========== ============ =================================
tee(2**33)>stdout $((2**33)) "x" # 1m50.605s / 0m01.496s / 0m27.774s
tee(2**30)>stdout -e $((2**30)) "xxx\txxx\n" # 1m49.055s / 0m01.560s / 0m27.750s
* 32GB
tee(2**35)>stdout -e $((2**35)) "x" #
tee(2**32)>stdout -e $((2**32)) "xxx\txxx\n" # 7m34.867s / 0m06.020s / 1m52.459s
python method: '.write' uses 'file.write()'
'>stcout' uses 'sys.stdout.write()'. It handles \n in args (but I know very little python)
count string real/user/sys
* 8GB ===== =================== =================================
python(2**33)a .write 2**33 "x" # OverflowError: repeated string is too long
python(2**33)a >stdout 2**33 "x" # OverflowError: repeated string is too long
python(2**30)b .write 2**30 '"xxxxxxxX" *2**0' # 6m52.576s / 6m32.325s / 0m19.701s
python(2**30)b >stdout 2**30 '"xxxxxxxX" *2**0' # 8m11.374s / 7m49.101s / 0m19.573s
python(2**30)c .write 2**20 '"xxxxxxxX" *2**10' # 2m14.693s / 0m03.464s / 0m22.585s
python(2**30)c >stdout 2**20 '"xxxxxxxX" *2**10' # 2m32.114s / 0m03.828s / 0m22.497s
python(2**30)d .write 2**10 '"xxxxxxxX" *2**20' # 2m16.495s / 0m00.024s / 0m12.029s
python(2**30)d >stdout 2**10 '"xxxxxxxX" *2**20' # 2m24.848s / 0m00.060s / 0m11.925s
python(2**30)e .write 2**0 '"xxxxxxxX" *2**30' # OverflowError: repeated string is too long
python(2**30)e >stdout 2**0 '"xxxxxxxX" *2**30' # OverflowError: repeated string is too long
* 32GB
python(2**32)f.write 2**12 '"xxxxxxxX" *2**20' # 7m58.608s / 0m00.160s / 0m48.703s
python(2**32)f>stdout 2**12 '"xxxxxxxX" *2**20' # 7m14.858s / 0m00.136s / 0m49.087s
perl method:
count string real / user / sys
* 8GB ===== =================== =================================
perl(2**33)a .syswrite> 2**33 "a" x 2**0 # Sloooooow! It would take 24 hours. I extrapolated after 1 hour.
perl(2**33)a >stdout 2**33 "a" x 2**0 # 31m46.405s / 31m13.925s / 0m22.745s
perl(2**30)b .syswrite> 2**30 "aaaaaaaA" x 2**0 # 100m41.394s / 11m11.846s / 89m27.175s
perl(2**30)b >stdout 2**30 "aaaaaaaA" x 2**0 # 4m15.553s / 3m54.615s / 0m19.949s
perl(2**30)c .syswrite> 2**20 "aaaaaaaA" x 2**10 # 1m47.996s / 0m10.941s / 0m15.017s
perl(2**30)c >stdout 2**20 "aaaaaaaA" x 2**10 # 1m47.608s / 0m12.237s / 0m23.761s
perl(2**30)d .syswrite> 2**10 "aaaaaaaA" x 2**20 # 1m52.062s / 0m10.373s / 0m13.253s
perl(2**30)d >stdout 2**10 "aaaaaaaA" x 2**20 # 1m48.499s / 0m13.361s / 0m22.197s
perl(2**30)e .syswrite> 2**0 "aaaaaaaA" x 2**30 # Out of memory during string extend at -e line 1.
perl(2**30)e >stdout 2**0 "aaaaaaaA" x 2**30 # Out of memory during string extend at -e line 1.
* 32GB
perl(2**32)f .syswrite> 2**12 "aaaaaaaA" x 2**20 # 7m34.241s / 0m41.447s / 0m51.727s
perl(2**32)f >stdout 2**12 "aaaaaaaA" x 2**20 # 10m58.444s / 0m53.771s / 1m28.498s