반복되는 자유 형식 문자열을 "dd"만큼 빠르게 파일에 쓰려면 어떻게 해야 합니까?

반복되는 자유 형식 문자열을 "dd"만큼 빠르게 파일에 쓰려면 어떻게 해야 합니까?

dd반복해서 쓸 수 있음\0 바이트파일을 매우 빠르게 쓰지만 반복되는 임의 문자열을 쓸 수는 없습니다.
당신은 가지고 있습니까?배쉬 쉘반복해서 쓰는 방법"dd"(포함)만큼 빠릅니까 \0?

Linux를 사용하면서 6개월 동안 제가 접한 모든 제안은 비슷 하지만 아래에 표시된 것과 printf "%${1}s" | sed -e "s/ /${2}/g"비교하면 고통스러울 정도로 느리고 약 384MB(내 상자에서) 후에 충돌이 발생합니다. 실제로 이것은 한 줄에 나쁘지 않습니다. 길이 :) - 하지만 무너지네요! 문자열에 개행 문자가 포함되어 있으면 문제가 되지 않을 것 같습니다.ddsed
sed

ddprintf+ 와의 속도 비교 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

일반적으로 쉘은 큰 데이터 블록을 처리하는 데 속도가 느린 것으로 알려져 있습니다. 대부분의 스크립트에서는 어떤 데이터 비트가 작을 가능성이 있고 어떤 비트가 클 가능성이 있는지 미리 알 수 있습니다.

  • 외부 프로세스를 포크하고 실행하면 지속적인 오버헤드가 발생하므로 작은 데이터를 처리하려면 내장된 셸을 사용하는 것이 좋습니다.
  • 전문화된 컴파일 도구가 해석된 범용 언어보다 더 효율적이므로 빅 데이터 처리를 위해 외부 전문 도구에 의존하는 것을 선호합니다.

ddread문제 및 통화 에 블록 크기를 사용하십시오 . writestrace(또는 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체인을 사용합니다 .teeteedd

# ============================================================================
# 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
#

시간 테스트 결과는 다음과 같습니다.. 제가 만들고 싶었던 테스트 파일의 크기와 비슷해서 32GB까지 늘렸습니다(그래서 이 문제를 해결하기 시작했습니다).

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

관련 정보