Универсальная альтернатива без времени `time`? [закрыто]

10

Для сравнения выполняется раз сценариев между различными оболочками, некоторые ответы SE предлагают использовать bash«ы встроенных time команд, например , так:

time bash -c 'foo.sh'
time dash -c 'foo.sh'

... и т. д. для каждой оболочки для тестирования. Такие тесты не устраняют время, необходимое для каждой оболочки, чтобы загрузить и инициализировать себя . Например, предположим, что обе вышеупомянутые команды были сохранены на медленном устройстве со скоростью чтения ранней дискеты (124 КБ / с) dash( исполняемый файл ~ 150 КБ) будет загружаться примерно в 7 раз быстрее, чем bash( ~ 1 М ) оболочка время загрузки искажает timeчисла - время предварительной загрузки этих оболочек не имеет отношения к измерению времени пробега foo.shпод каждой оболочкой после загрузки оболочек.

Какой самый лучший портативный и вообще Util для запуска для синхронизации сценария , который может быть запущен из внутри каждой оболочки? Таким образом, приведенный выше код будет выглядеть примерно так:

bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'

NB: нет встроенных time команд оболочки , поскольку ни одна из них не является переносимой или общей.


Еще лучше, если утилита также может измерять время, затрачиваемое внутренними командами и конвейерами оболочки, без необходимости пользователю сначала заключать их в сценарий. Искусственный синтаксис, подобный этому, поможет:

general_timer_util "while read x ; do echo x ; done < foo"

Некоторые снаряды timeмогут справиться с этим. Например bash -c "time while false ; do : ; done"работает. Чтобы увидеть, что работает (и не работает) в вашей системе, попробуйте:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done
АРУ
источник
6
Просто использовать /usr/bin/time?
Кусалананда
1
Я не понимаю, как любой не встроенный модуль мог бы «исключить время, затрачиваемое на загрузку и инициализацию каждой оболочки», и выполнить автономный скрипт, будучи «переносимым и общим».
Майкл Гомер
1
Это не ответ на вопрос, это подсказка для вас, чтобы уточнить, что вы хотите.
Майкл Гомер
1
Я опубликовал свою лучшую попытку, но я думаю, что вопрос все еще недостаточно конкретизирован относительно того, чего именно он на самом деле пытается достичь.
Майкл Гомер
2
Что вы подразумеваете под «портативный или общий»? Встроенные оболочки являются такими же переносимыми (работают на таком же количестве систем) и более общими (работают в большем количестве обстоятельств, поскольку они могут рассчитывать время, отличное от выполнения файла), чем внешняя команда. Какую проблему ты пытаешься решить?
Жиль "ТАК - перестань быть злым"

Ответы:

10

Следует отметить, что timeэто указано в POSIX , а AFAICT - единственная опция, о которой упоминает POSIX ( -p), корректно поддерживается различными оболочками:

$ bash -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'

real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'       

real 0.00
user 0.00
sys 0.00
Мур
источник
1
Проблема в том, что для того, чтобы можно было сравнивать время, результат должен быть рассчитан на ту же реализацию time. Это сравнимо с тем, что спринтеры могут самостоятельно измерять время на 100 м вместо того, чтобы делать это одновременно с одним часом. Это явно придирчиво, но все же ...
Кусалананда
@Kusalananda Я думал, что проблема в том, что мысль OP timeне переносима; кажется, что он портативный. (Я согласен с вашей точкой зрения на сопоставимость)
muru
@muru, на моей системе dash -c 'time -p while false ; do : ; done'возвращает «время: не может работать , пока: Нет такого файла или каталога <CR> Команда не вышла с ненулевым статусом 127» ошибок.
АРУ
1
@agc POSIX также говорит: «Термин утилита используется, а не команда, чтобы подчеркнуть тот факт, что составные команды оболочки, конвейеры, специальные встроенные модули и т. д. не могут использоваться непосредственно. Однако утилита включает в себя пользовательские прикладные программы и сценарии оболочки, а не только стандартные утилиты. " (см. раздел ОБОСНОВАНИЕ)
Муру
7

Я использую команду GNU date , которая поддерживает таймер высокого разрешения:

START=$(date +%s.%N)
# do something #######################

"$@" &> /dev/null

#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"

И тогда я называю сценарий так:

/usr/local/bin/timing dig +short unix.stackexchange.com
141.835

Единица вывода в миллисекундах.

Рабин
источник
1
Предполагая, что время (время эпохи) не изменяется между ними. Не могу вспомнить случай на практике, где это может вызвать проблемы, но все же стоит упомянуть.
PhK
1
Вы должны добавить, что это требует GNU dateспециально.
Кусалананда
@phk, пожалуйста, объясните?
Рабин
1
@Rabin Скажем, ваш NTP-клиент выдает, обновляет и меняет ваши часы между тем, где STARTи ENDустановлено, тогда это, очевидно, повлияет на ваш результат. Не знаю, насколько вам это нужно и имеет ли это значение в вашем случае, но, как я уже сказал, кое-что нужно иметь в виду. (Забавная история: я знаю программное обеспечение, в котором именно оно приводило к неожиданно отрицательным результатам - оно использовалось для вычисления пропускной способности, - которое затем сломало несколько вещей.)
phk
1
Кроме того, разве некоторые NTP-клиенты не замедляют и не ускоряют время, вместо того, чтобы делать «скачки» в системном времени? Если у вас есть такой NTP-клиент, а вчера вечером вы установили некоторые временные параметры, они, вероятно, искажены тем, что NTP-клиент «ожидает» високосную секунду. (Или системные часы просто работают до 61 в этом случае?)
Jörg W Mittag
6

Как timeвы заметили, утилита обычно встроена в оболочку, что делает ее бесполезной в качестве «нейтрального» таймера.

Однако эта утилита обычно также доступна в качестве внешней утилиты, /usr/bin/timeкоторую вполне можно использовать для выполнения предлагаемых вами временных экспериментов.

$ bash -c '/usr/bin/time foo.sh'
Кусалананда
источник
Как это «исключает время, затрачиваемое на загрузку и инициализацию каждой оболочки»?
Майкл Гомер
1
Если foo.shон исполняемый и имеет шебанг, то он всегда запускает его с одной и той же оболочкой и считает время запуска этой оболочки, так что это не то, что хочет OP. Если foo.shотсутствует один из них, то это не работает вообще.
Кевин
@Kevin Очень верно. timeКажется, я принял во внимание только «встроенную оболочку ». Время запуска оболочки может быть измерено отдельно.
Кусалананда
1
Я не знаю ни одной оболочки, которая имеет timeвстроенную команду. Однако во многих оболочках, в том числе, bashесть timeключевое слово, которое можно использовать для определения времени конвейеров. Чтобы отключить это ключевое слово и использовать timeкоманду (в файловой системе), вы можете заключить его в кавычки "time" foo.sh. См. Также unix.stackexchange.com/search?q=user%3A22565+time+keyword
Стефан
6

Вот решение, которое:

  1. исключить [s] время, необходимое для каждой оболочки, чтобы загрузить и инициализировать себя

  2. может быть запущен из внутри каждой оболочки

  3. Пользы

    нет встроенных timeкоманд оболочки , так как ни одна из них не является переносимой или общей

  4. Работает во всех POSIX-совместимых оболочках.
  5. Работает на всех POSIX-совместимых и XSI-совместимых системах с компилятором C или там, где вы можете заранее скомпилировать исполняемый файл C.
  6. Использует одинаковую временную реализацию на всех оболочках.

Он состоит из двух частей: короткой программы на C, которая закрывается gettimeofday, которая устарела, но все еще более переносимой clock_gettime, и короткого сценария оболочки, который использует эту программу для получения часов с микросекундной точностью, считывающих обе стороны поиска сценария. Программа на C - это единственный переносимый способ с минимальными издержками, позволяющий получить метку времени с точностью до секунды.

Вот программа на C epoch.c:

#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
    struct timeval time;
    gettimeofday(&time, NULL);
    printf("%li.%06i", time.tv_sec, time.tv_usec);
}

И скрипт оболочки timer:

#!/bin/echo Run this in the shell you want to test

START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc

Это стандартный командный язык оболочки, и он bcдолжен работать как скрипт в любой POSIX-совместимой оболочке.

Вы можете использовать это как:

$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662

Он не измеряет системное или пользовательское время, только немонотонные настенные часы. Если системные часы изменятся во время выполнения скрипта, это даст неверные результаты. Если система находится под нагрузкой, результат будет ненадежным. Я не думаю, что что-то лучше может быть переносимым между снарядами.

evalВместо этого можно использовать модифицированный скрипт таймера для запуска команд вне скрипта.

Майкл Гомер
источник
По совпадению, как раз перед прочтением этого (и последней строки о нем eval) я настраивал сценарий в ответе Рабинаeval "$@" , чтобы он включал его , чтобы он мог запускать встроенные оболочки на лету.
АРУ
4

Многократно пересмотренное решение с использованием /proc/uptimeи dc/ bc/ awkбольшими частями благодаря вкладу agc :

#!/bin/sh

read -r before _ < /proc/uptime

sleep 2s # do something...

read -r after _ < /proc/uptime

duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
#   duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
#   duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')

echo "It took $duration seconds."

Предполагается, очевидно, что /proc/uptimeсуществует и имеет определенную форму.

PHK
источник
3
Это сделало бы его переносимым между оболочками, но не переносимым между реализациями Unix, поскольку в некоторых просто отсутствует /procфайловая система. Если это проблема или нет, я не знаю.
Кусалананда
1
Для акцента эта буква Q предлагает скорость дискеты или хуже. В этом случае накладные расходы на загрузку awkмогут быть значительными. Может быть, b=$(cat /proc/uptime)до, потом a=$(cat /proc/uptime)после, затем проанализируем $ a и $ b и вычтем.
АРУ
@agc Хороший вклад, спасибо, я добавил несколько альтернативных решений соответственно.
phk
1
Не думать об этом раньше, но если встроенные команды любят readлучше , чем catэто было бы чище (и немного быстрее): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
АРУ