Можно получить количество секторов на диске с hdparm -v /dev/sdx
. Попытайтесь искать на number_of_sectors - 2048 (принятие 512-байтовых секторов).
Я не сделал сравнительного теста, но я вижу несколько потенциальных улучшений.
Вы открываете и закрываете файл для каждого вызова к 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 (но специализированные утилиты обычно быстрее при установке их вариантам использования).
Мы знаем, что это медленно от выполнения:
$ 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.
date
, но перечисление в оболочке, а не seq
)? Каковы Ваши системные характеристики (в особенности, ядро и файловая система могут сильно повлиять на затраты на разветвление и доступы к файлу)?
– Gilles 'SO- stop being evil'
02.03.2011, 23:41
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
date
) подвергнитесь серьезному (смейте, я говорю серьезный), наверху... Мой сценарий выпускает системный вызов каждой строки в журнале.. Принимая во внимание, что в Вашем случае Вы делаете в общей сложности 2 системных вызова для 'датирований'; 1 для фидера, и 1 для самого журнала.. ЧТО И ТРЕБОВАЛОСЬ ДОКАЗАТЬ!.. Эти 57 минут были уменьшены до 1 минуты (Это должно оценить как классический пример того, почему не выполнить внешние вызовы в волей-неволей способ... Я ссылаюсь на незнание закона :)
– Peter.O
27.02.2011, 14:20
date
, Вы делаете 100000000 (или безотносительно) добавляет для регистрации datein.txt. тогда как метод Gilles использует 1 вызов для seq
и затем оболочка просто передает по каналу, это - поток вывода к date
.... Ничего себе! Мне нужен кофе! но что большой урок...
– Peter.O
28.02.2011, 11:44
date
также должен считать тот файл 100000000 времена (?).. независимо от того, что точные детали, кажется, что использование оболочки непосредственно является сообщением взятия домой.... Я не сделал своего редактирования вовремя (снова ;)
– Peter.O
28.02.2011, 11:50
seq
не принимает целочисленные форматы.%12.0f
добьется цели до года 33658. Большая часть архитектуры имеет 52-разрядную мантиссу с плавающей точкой, таким образом, можно подойти%15.0f
и все еще смочь выразить целые числа точно. – Gilles 'SO- stop being evil' 27.02.2011, 15:30eval echo -n @{$secBeg..$secEnd}\$\'\\n\'
.... это добавляет ведущее пространство ко всей панели первая строка, но это - хорошо для этой ситуации... ;) – Peter.O 27.02.2011, 15:40seq
одно только расширение (к/dev/null) занимает 14 минут... Все задание создания файла на 8 ГБ заняло 45 минут. – Peter.O 28.02.2011, 10:27