Быстрый способ создать тестовый файл с каждой секундой, перечисленной в YYYY-mm-dd HH:MM:SS формат

Можно получить количество секторов на диске с hdparm -v /dev/sdx. Попытайтесь искать на number_of_sectors - 2048 (принятие 512-байтовых секторов).

6
27.02.2011, 13:31
3 ответа

Я не сделал сравнительного теста, но я вижу несколько потенциальных улучшений.

Вы открываете и закрываете файл для каждого вызова к 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 (но специализированные утилиты обычно быстрее при установке их вариантам использования).

8
27.01.2020, 20:22
  • 1
    Спасибо Gilles... Хорошо, что мы учимся нашими ошибками... (Я изучал много в последнее время :)... Я, конечно, более знаю теперь об этих 'внутренних опциях пакета', Вы упомянули... –  Peter.O 27.02.2011, 14:32
  • 2
    OMG!... В 500 раз быстрее, чем мой сценарий! 8,6 секунд (seq нужна некоторая тонкая настройка.. Это производит экспоненциальное представление. Я использовал клудж расширения фигурной скобки, потому что я не мог определить право seq опция.)... –  Peter.O 27.02.2011, 15:19
  • 3
    @fred.bear: А-ч, да, это является раздражающим это seq не принимает целочисленные форматы. %12.0f добьется цели до года 33658. Большая часть архитектуры имеет 52-разрядную мантиссу с плавающей точкой, таким образом, можно подойти %15.0f и все еще смочь выразить целые числа точно. –  Gilles 'SO- stop being evil' 27.02.2011, 15:30
  • 4
    Это - то, что я использовал: eval echo -n @{$secBeg..$secEnd}\$\'\\n\' .... это добавляет ведущее пространство ко всей панели первая строка, но это - хорошо для этой ситуации... ;) –  Peter.O 27.02.2011, 15:40
  • 5
    Для заинтересованных, вот то, как это удалось с помощью метода Gilles... Для готового изделия 429520320 (429 миллионов) строки; т.е. 13,6 лет дат (одна секунда на строку):... seq одно только расширение (к/dev/null) занимает 14 минут... Все задание создания файла на 8 ГБ заняло 45 минут. –  Peter.O 28.02.2011, 10:27

Мы знаем, что это медленно от выполнения:

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

(и это - версия, которая только печатает 4 часа, не 2 года.)

Получить лучшее понимание где 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

Но ответ Жабр находит способ только работать 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 по сравнению с 28 806 и 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

Который является также очень, намного быстрее, чем исходный сценарий, но немного медленнее что Gilles.

4
27.01.2020, 20:22
  • 1
    @Mikel:Мило. Вы могли также попробовать ответ Iain (единственный вызов к date, но перечисление в оболочке, а не seq)? Каковы Ваши системные характеристики (в особенности, ядро и файловая система могут сильно повлиять на затраты на разветвление и доступы к файлу)? –  Gilles 'SO- stop being evil' 02.03.2011, 23:41
  • 2
    Уверенный. Это - Ubuntu 10.10, 32-разрядная с 2.6.35-25-универсальным Linux, ext4/tmp, в двухъядерной системе AMD Athlon X2. –  Mikel 02.03.2011, 23:55
  • 3
    Большой анализ - Является циклом с условием продолжения быстрее, чем для цикла? –   03.03.2011, 00:10
  • 4
    @Iain: Среднее число 5 выполнений, самых низких и самых высоких удаленный: в течение loop=0.752666 секунды, в то время как loop=0.751333 секунда. Таким образом, я не думаю, что существует различие. –  Mikel 03.03.2011, 00:34
  • 5
    Спасибо это интересно знать. Из любопытства я поднял простую c программу для генерации секунд, которые передаются по каналу прямо в date -f - ... и получите файл журнала размера 1261440020 в 6m48, который является о 10.8Gb/Hr на моей одноядерной Ubuntu VM. –   03.03.2011, 00:42

Этот сценарий генерирует 10 миллионов файлов строки 201 МБ в 7m50.0 s на VM, который я имею удобный. Это о 1.5Gb/hr.

#!/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
3
27.01.2020, 20:22
  • 1
    @Iain: Превосходный, Спасибо... Это очевидно, и я не мог видеть его, даже при том, что я думал об этом в последнее время... т.е. как системные вызовы вне собственной среды удара (напримеркому: date) подвергнитесь серьезному (смейте, я говорю серьезный), наверху... Мой сценарий выпускает системный вызов каждой строки в журнале.. Принимая во внимание, что в Вашем случае Вы делаете в общей сложности 2 системных вызова для 'датирований'; 1 для фидера, и 1 для самого журнала.. ЧТО И ТРЕБОВАЛОСЬ ДОКАЗАТЬ!.. Эти 57 минут были уменьшены до 1 минуты (Это должно оценить как классический пример того, почему не выполнить внешние вызовы в волей-неволей способ... Я ссылаюсь на незнание закона :) –  Peter.O 27.02.2011, 14:20
  • 2
    @Iain: я был поражен Вашим сценарием, и в 57 раз быстрее очень хорошо, но Gilles придумал сценарий, который означает, что он должен получить 'лучший' ответ... Сценарий Gille срезал его к 8,6 секундам! (для количества строк в моем примере)... приблизительно в 500 раз быстрее, чем мой сценарий! –  Peter.O 27.02.2011, 15:28
  • 3
    @Iain: я весь день был озадачивающим (хорошо, не весь день, но на и прочь) законченный, почему существует такое различие в разнице во времени beteween Ваш метод и Gilles... Я просто определил его (возможно??)... Хотя Вы выполняете только 2 вызова к date, Вы делаете 100000000 (или безотносительно) добавляет для регистрации datein.txt. тогда как метод Gilles использует 1 вызов для seq и затем оболочка просто передает по каналу, это - поток вывода к date.... Ничего себе! Мне нужен кофе! но что большой урок... –  Peter.O 28.02.2011, 11:44
  • 4
    @fred.bear: Да, использование метода, я предложил генерировать входной файл, занимает много времени. Команда даты занимает относительно мало времени. –   28.02.2011, 11:45
  • 5
    PS.. и date также должен считать тот файл 100000000 времена (?).. независимо от того, что точные детали, кажется, что использование оболочки непосредственно является сообщением взятия домой.... Я не сделал своего редактирования вовремя (снова ;) –  Peter.O 28.02.2011, 11:50

Теги

Похожие вопросы