YYYY-mm-dd HH:MM:SS形式で1秒あたりにリストされるテストファイルを作成する簡単な方法

YYYY-mm-dd HH:MM:SS形式で1秒あたりにリストされるテストファイルを作成する簡単な方法

秒単位でリストされている日付行を持つ大きなテストファイルを作成したいのですが、私のアプローチは非常に長い時間がかかります...(または少なくともその感じです:)... 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各回線に別々の電話をかけています。 Unixは外部プログラムをすばやく呼び出すのに精通していますが、内部プログラムは依然として優れています。 GNU日付にはバッチオプションがあります。標準入力に日付を入力すると、日付がきれいに印刷されます。また、整数の範囲を列挙するにはを使用してください。seqこれは、シェルでループを解釈するよりも高速です。

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

通常、シェルスクリプトが遅すぎる場合は、専用ユーティリティで内部ループを実行してみてください。ここではとですseqが、date一般的に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

したがって、我々は上位2つの呼び出しがあることを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の答えはランだけを実行する方法を見つけ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

7waitpidsと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

だから現れていますcloseopen

>>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秒で1000万行の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

関連情報