YYYY-mm-dd HH:MM:SS 형식으로 초당 나열되는 테스트 파일을 작성하는 빠른 방법

YYYY-mm-dd HH:MM:SS 형식으로 초당 나열되는 테스트 파일을 작성하는 빠른 방법

초 단위로 나열된 날짜 행이 있는 큰 테스트 파일을 만들고 싶지만 내 접근 방식은 매우 오랜 시간이 걸립니다... (또는 적어도 그 느낌입니다 :) ... 1051201 행만 만드는 데 43분이 걸립니다. 20.1MB 파일....

각 행의 날짜가 고유한 더 큰 파일을 만들고 싶습니다.
제가 처리하는 방식보다 더 빠른 방법이 있나요? :

# # BEGIN CREATE TEST DATA  ============ 
# # Create some dummy data.
  file=/tmp/$USER/junk
  ((secY2 =s3600*24*365*2))
  cnt=0
  secBeg=$(date --date="2010-01-01 00:00:00" +%s)
  secEnd=$((secBeg+secY2))
  ((sec=secBeg))
  while ((sec<=secEnd)) ; do
      date -d '1970-01-01 UTC '$sec' seconds' '+%Y-%m-%d %H:%M:%S' >>"$file" 
      ((sec+=1))
      ((cnt+=1))
  done
  ls -l "$file"
  echo Lines written: $cnt
# END CREATE TEST DATA  ============

답변1

아직 벤치마킹을 수행하지는 않았지만 몇 가지 잠재적인 개선이 있을 것으로 보입니다.

호출할 때마다 파일을 열고 닫습니다 date. 이것은 낭비입니다. 전체 루프에 리디렉션을 넣으십시오.

while …; do …; done >"$file"

date각 회선에 별도의 전화를 걸고 있습니다. 유닉스는 외부 프로그램을 빠르게 호출하는 데 능숙하지만 내부 프로그램은 여전히 ​​더 좋습니다. GNU 날짜에는 배치 옵션이 있습니다. 표준 입력에 날짜를 입력하면 날짜가 예쁘게 인쇄됩니다. 또한 정수 범위를 열거하려면 를 사용하세요. seq이는 셸에서 루프를 해석하는 것보다 더 빠를 수 있습니다.

seq -f @%12.0f $secBeg $secEnd | date -f - '+%Y-%m-%d %H:%M:%S' >"$file"
cnt=$(($secY2 + 1))

일반적으로 쉘 스크립트가 너무 느린 경우 전용 유틸리티에서 내부 루프를 실행해 보십시오. 여기서는 seqdate이지만 일반적으로 sed또는 입니다 awk. 그렇게 할 수 없다면 Perl이나 Python과 같은 더 높은 수준의 스크립팅 언어로 전환하십시오(그러나 사용 사례에 적합하면 전용 유틸리티가 더 빠른 경우가 많습니다).

답변2

우리는 그것이 느리게 실행된다는 것을 알고 있습니다:

$ time ./junk.sh
Lines written: 14401
./junk.sh  2.27s user 3.31s system 21% cpu 25.798 total

(이 버전은 2년이 아닌 4시간만 인쇄합니다.)

시간이 어디에 소비되는지 더 잘 이해하기 위해 bash를 사용할 수 있습니다 strace -c.

$ strace -c ./junk.sh
Lines written: 14401
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 79.01    0.128906           4     28806     14403 waitpid
 17.92    0.029241           2     14403           clone
  2.45    0.003999           0    158448           rt_sigprocmask
  0.33    0.000532           0     28815           rt_sigaction
  0.29    0.000479           0     14403           sigreturn

따라서 우리는 상위 두 호출이 waitpid및 임을 알 수 있습니다 clone. 그 자체로는 많은 시간을 차지하지 않지만(단지 0.128906초와 0.029241초) 많이 호출되는 것을 볼 수 있으므로 문제는 date각 숫자를 에코하기 위해 별도의 명령을 실행해야 한다는 점이라고 의심됩니다.

bash그런 다음 몇 가지 검색을 수행한 결과 다음을 통해 gprof지원되도록 컴파일 할 수 있음을 발견했습니다 .

$ ./configure --enable-profiling --without-bash-malloc
$ make

이제 사용해 보세요:

$ ./bash-gprof junk.sh
Lines written: 14401
$ gprof ./bash-gprof gmon.out

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
  8.05      0.28     0.28    14403     0.00     0.00  make_child
  6.61      0.51     0.23                             __gconv_transform_utf8_internal
  5.75      0.71     0.20                             fork
  5.75      0.91     0.20   259446     0.00     0.00  hash_search
  5.17      1.09     0.18   129646     0.00     0.00  dispose_words

bash따라서 함수 이름이 의미가 있다고 가정하면 외부 명령을 반복적으로 분기하고 호출하는 것이 문제라는 것을 확인할 수 있습니다 .

>>루프 의 끝 으로 이동하면 while거의 효과가 없습니다.

$ time ./junk2.sh
...
./junk2.sh  2.46s user 3.18s system 22% cpu 25.659 total

그러나 Giles의 답변은 run 만 실행하는 방법을 찾았 date으며 놀랍게도 그것은많은서둘러요:

$ time ./bash-gprof junk3.sh
Lines written: 14401
./bash-gprof junk3.sh  0.10s user 0.16s system 96% cpu 0.264 total

$ strace -c ./bash-gprof junk3.sh
Lines written: 14401
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.63    0.039538        5648         7         3 waitpid
  2.37    0.000961          37        26           writev
  0.00    0.000000           0         9           read
  ...
  0.00    0.000000           0         4           clone


$ gprof ./bash-gprof gmon.out 
Flat profile:

Each sample counts as 0.01 seconds.
 no time accumulated

  %   cumulative   self              self     total           
 time   seconds   seconds    calls  Ts/call  Ts/call  name    
  0.00      0.00     0.00     1162     0.00     0.00  xmalloc
  0.00      0.00     0.00      782     0.00     0.00  mbschr
  0.00      0.00     0.00      373     0.00     0.00  shell_getc

7 waitpids과 4를 clones원본의 28806과 14403과 비교해보세요!

따라서 교훈은 다음과 같습니다. 여러 번 반복되는 루프 내에서 외부 명령을 호출해야 하는 경우 이를 루프 밖으로 이동하는 방법을 찾거나 호출 프로그래밍 언어가 필요하지 않은 외부 명령으로 전환해야 합니다. 작업을 수행합니다.


요청에 따라 Iain의 방법을 기반으로 한 테스트(동일한 변수 이름과 루프를 사용하도록 수정됨):

#!/bin/bash
datein=junk.$$.datein
file=junk.$$
((secY2=3600*4))
cnt=0
secBeg=$(date --date="2010-01-01 00:00:00" +%s)
secEnd=$((secBeg+secY2))
((sec=secBeg))
while ((sec<=secEnd)) ; do
  echo @$sec >>"$datein"
  ((sec+=1))
  ((cnt+=1))
done
date --file="$datein" '+%Y-%m-%d %H:%M:%S' >>"$file"
ls -l "$file"
rm "$datein"
echo Lines written: $cnt

결과:

$ time ./bash-gprof ./junk4.sh 
Lines written: 14401
./bash-gprof ./junk4.sh  0.92s user 0.20s system 94% cpu 1.182 total

$ strace -c ./junk4.sh
Lines written: 14401
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 91.71    0.116007       14501         8         4 waitpid
  3.70    0.004684           0     14402           write
  1.54    0.001944           0     28813           close
  1.35    0.001707           0     72008         1 fcntl64
  0.88    0.001109           0     43253           rt_sigprocmask
  0.45    0.000566           0     28803           dup2
  0.36    0.000452           0     14410           open

$ gprof ./bash-gprof gmon.out 
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 22.06      0.15     0.15                             __gconv_transform_utf8_internal
 16.18      0.26     0.11                             mbrtowc
  7.35      0.31     0.05                             _int_malloc
  5.88      0.35     0.04                             __profile_frequency
  4.41      0.38     0.03   345659     0.00     0.00  readtok
  4.41      0.41     0.03                             _int_free
  2.94      0.43     0.02   230661     0.00     0.00  hash_search
  2.94      0.45     0.02    28809     0.00     0.00  stupidly_hack_special_variables
  1.47      0.46     0.01   187241     0.00     0.00  cprintf
  1.47      0.47     0.01   115232     0.00     0.00  do_redirections

그래서 나타나고 있습니다 close.open

>>Eelvex는 이제 각 라인과 루프 >주변의 관찰 에 영향을 미치기 시작합니다 while.

그 점을 고려해보자...

#!/bin/bash
datein=junk.$$.datein
file=junk.$$
((secY2=3600*4))
cnt=0
secBeg=$(date --date="2010-01-01 00:00:00" +%s)
secEnd=$((secBeg+secY2))
for ((sec=secBeg; sec<=secEnd; sec=sec+1)) ; do
  echo @$sec
  ((cnt+=1))
done >"$datein"
date --file="$datein" '+%Y-%m-%d %H:%M:%S' >>"$file"
ls -l "$file"
rm "$datein"
echo Lines written: $cnt

$ time ./junk6.sh
Lines written: 14401
./junk6.sh  0.58s user 0.14s system 95% cpu 0.747 total

$ strace -c junk6.sh
Lines written: 14401
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.41    0.092263       11533         8         4 waitpid
  2.06    0.001949           0     43252           rt_sigprocmask
  0.53    0.000506           0     14402           write
  0.00    0.000000           0        13           read
  0.00    0.000000           0        10           open
  0.00    0.000000           0        13           close
  0.00    0.000000           0         1           execve

$ gprof ./bash-gprof gmon.out
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ms/call  ms/call  name    
 10.00      0.05     0.05    72025     0.00     0.00  expand_word_internal
 10.00      0.10     0.05                             __gconv_transform_utf8_internal
  8.00      0.14     0.04                             __profile_frequency
  8.00      0.18     0.04                             _int_malloc
  4.00      0.20     0.02  1355024     0.00     0.00  xmalloc
  4.00      0.22     0.02   303217     0.00     0.00  mbschr

이것도 많이,많은원본 스크립트보다 빠르지만 Giles의 스크립트보다 약간 느립니다.

답변3

이 스크립트는 제가 가지고 있는 가상 머신에서 7분 50초 만에 천만 줄의 201Mb 파일을 생성했습니다. 약 1.5Gb/시간.

#!/bin/bash
Tstart=$(date +%s)
let Tend=$Tstart+100000000

[ -e datein.txt ] && rm datein.txt
[ -e logfile.log ] && rm logfile.log

for (( Tloop=Tstart; Tloop <=Tend; Tloop++ ))
do
    echo @$Tloop >> datein.txt
done

date --file=datein.txt '+%Y-%m-%d %H:%M:%S' >>logfile.log

관련 정보