Обратите внимание, что ваш цикл немного избыточен, for
установит guess
для всех значений, напечатанных seq
, вы не нужно изменить значение самостоятельно.
for guess in $(seq -f "%04g" 0000 9999) ;do
guess=$((10#guess+1))
done
Фактически, цикл for
в значительной степени отменяет сделанное вами присвоение, сравните с этим:
$ for x in 9 6 3 ; do echo $x; x=1 ; done
9
6
3
(Назначенное значение 1
никогда не печатается.)
Итак, достаточно этого:
$ for x in $(seq -f "%04g" 1 2) ; do echo $x; done
0001
0002
Или вы можете использовать printf
, чтобы получить число в нужном вам формате:
$ for x in $(seq 1 2) ; do printf "%04d\n" $x; done
0001
0002
Я думаю, что в некоторых системах нет seq
, так что вам придется обойтись без. В Bash это должно работать:
$ for (( x=0 ; x < 100 ; x++ )) ; do
printf "%04d\n" $x
done
Это известная ошибка в bash; см. справочную страницу и выполните поиск по запросу "ОШИБКИ":
ОШИБКИ Он слишком большой и слишком медленный.
;)
Отличный учебник по концептуальным различиям между оболочками сценариев и других языков программирования, я настоятельно рекомендую прочитать:
Наиболее уместные выдержки:
Оболочки - это язык более высокого уровня. Можно сказать, это даже не язык. Они стоят раньше всех интерпретаторов командной строки. Работа выполняется теми командами, которые вы запускаете, и оболочка предназначена только для их согласования.
...
IOW, в оболочках, особенно для обработки текста, вы вызываете как можно меньше утилит и заставляете их взаимодействовать с задачей, а не запускать тысячи инструментов по очереди, ожидая, пока каждый из них запустится, запустится, очистите перед запуском следующего.
...
Как было сказано ранее, выполнение одной команды имеет определенную стоимость. Огромные затраты, если эта команда не встроена, но даже если они встроены, стоимость велика.
И оболочки не предназначены для такой работы, они не претендуют на то, чтобы быть высокопроизводительными языками программирования. Это не так, это просто интерпретаторы командной строки. Итак, на этом фронте была проведена небольшая оптимизация.
Не используйте большие циклы в сценариях оболочки.
Циклы оболочки медленные, а цикл bash - самый медленный. Оболочки не предназначены для выполнения тяжелой работы в циклах. Оболочки предназначены для запуска нескольких внешних оптимизированных процессов над пакетами данных.
В общем, мне было любопытно, как сравниваются циклы оболочки, поэтому я провел небольшой тест:
#!/bin/bash
export IT=$((10**6))
echo POSIX:
for sh in dash bash ksh zsh; do
TIMEFORMAT="%RR %UU %SS $sh"
time $sh -c 'i=0; while [ "$IT" -gt "$i" ]; do i=$((i+1)); done'
done
echo C-LIKE:
for sh in bash ksh zsh; do
TIMEFORMAT="%RR %UU %SS $sh"
time $sh -c 'for ((i=0;i<IT;i++)); do :; done'
done
G=$((10**9))
TIMEFORMAT="%RR %UU %SS 1000*C"
echo 'int main(){ int i,sum; for(i=0;i<IT;i++) sum+=i; printf("%d\n", sum); return 0; }' |
gcc -include stdio.h -O3 -x c -DIT=$G -
time ./a.out
( Подробности:
)
Результаты (сокращенные) (время на итерацию):
POSIX:
5.8 µs dash
8.5 µs ksh
14.6 µs zsh
22.6 µs bash
C-LIKE:
2.7 µs ksh
5.8 µs zsh
11.7 µs bash
C:
0.4 ns C
Из результатов:
Если вам нужен немного более быстрый цикл оболочки, то если у вас есть синтаксис [[
] и вам нужен быстрый цикл оболочки, вы работаете в расширенной оболочке, и у вас также есть цикл for в стиле Си. Тогда используйте цикл C как for. Они могут быть примерно в 2 раза быстрее, чем , в то время как [
-циклы в той же оболочке.
цикл for (
примерно 2,7 мкс на итерацию цикл while [
] примерно 5,8 мкс на итерацию Циклы C for могут быть на 3-4 порядка десятичной величины быстрее (я слышал, что Торвальдс любит C).
Оптимизированный цикл C for в 56500 раз быстрее чем цикл bash while [
(самый медленный цикл оболочки) и в 6750 раз быстрее, чем цикл ksh for (
(самый быстрый цикл оболочки).
Опять же, медленность оболочек не должна иметь большого значения, потому что типичный паттерн с оболочками заключается в разгрузке нескольких процессов внешних оптимизированных программ.
Благодаря этому шаблону оболочки часто значительно упрощают написание скриптов с производительностью, превосходящей скрипты Python (в прошлый раз, когда я проверял, создание конвейеров процессов в Python было довольно неуклюжим).
Еще одна вещь, которую следует учитывать, - это время запуска.
time python3 -c ' '
на моем ПК занимает от 30 до 40 мс, тогда как оболочка занимает около 3 мс. Если вы запускаете много скриптов, это быстро накапливается, и вы можете сделать очень много за дополнительные 27–37 мсек, которые требуется python только для запуска. Небольшие скрипты можно закончить несколько раз за это время.
(NodeJs, вероятно, является худшей средой выполнения сценариев в этом отделе, так как для запуска требуется около 100 мсек (даже если после запуска вам будет сложно найти лучшего исполнителя среди языков сценариев)).
Оболочка эффективна, если вы используете ее для того, для чего она была разработана (хотя эффективность - это редко то, что вы ищете в оболочке).
Оболочка - это интерпретатор командной строки, он предназначен для выполнения команд и их совместной работы над задачей.
Если вы хотите сосчитать до 1000000000, вы вызываете (одну) команду для счета, например seq
, bc
, awk
или python
/perl
... Выполнение 1000000000 [[...]]
команд и 1000000000 let
команд будет ужасно неэффективным, особенно с bash
, который является самой медленной оболочкой из всех.
В этом отношении shell будет намного быстрее:
$ time sh -c 'seq 100000000' > /dev/null
sh -c 'seq 100000000' > /dev/null 0.77s user 0.03s system 99% cpu 0.805 total
$ time python -c 'i=0
> while i <= 100000000: i=i+1'
python -c 'i=0 while i <= 100000000: i=i+1' 12.12s user 0.00s system 99% cpu 12.127 total
Хотя, конечно, большую часть работы выполняют команды, которые shell вызывает, как и должно быть.
Конечно, то же самое можно сделать с python
:
python -c '
import os
os.dup2(os.open("/dev/null", os.O_WRONLY), 1);
os.execlp("seq", "seq", "100000000")'
Но в python
это не совсем так, поскольку python
- это в первую очередь язык программирования, а не интерпретатор командной строки.
Обратите внимание, что вы можете сделать:
python -c 'import os; os.system("seq 100000000 > /dev/null")'
Но, python
фактически будет вызывать оболочку для интерпретации командной строки!
Я заметил резкое отличие в bash от использования логически эквивалентных выражений «пока» и «до»:
time (i=0 ; while ((i<900000)) ; do i=$((i+1)) ; done )
real 0m5.339s
user 0m5.324s
sys 0m0.000s
time (i=0 ; until ((i=900000)) ; do i=$((i+1)) ; done )
real 0m0.000s
user 0m0.000s
sys 0m0.000s
Не то чтобы это действительно очень важно для вопроса, кроме этого, возможно, иногда небольшие различия имеют большое значение, даже если мы ожидаем, что они будут эквивалентны.
Все в порядке (кроме ваших ожиданий), поскольку python действительно довольно быстр для некомпилированного языка, см. https://wiki.python.org/moin/PythonSpeed
Ответ: Bash намного медленнее Python.
Один небольшой пример - в сообщении в блоге Производительность нескольких языков .
Помимо комментариев, вы могли бы оптимизировать код немного, например,
#!/bin/bash
for (( i = 0; i <= 1000000000; i++ ))
do
: # null command
done
Этот код должен занимать немного меньше времени.
Но, очевидно, недостаточно быстро, чтобы быть действительно пригодным для использования.
Я провел небольшое тестирование и в своей системе выполнил следующее - ни один из них не увеличил скорость на порядок, необходимый для конкурентоспособности, но вы можете сделать это быстрее:
Тест 1: 18,233 с
#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do
let i++
done
test2: 20.45s
#!/bin/bash
i=0
while [[ $i -le 4000000 ]]
do
i=$(($i+1))
done
test3: 17.64s
#!/bin/bash
i=0
while [[ $i -le 4000000 ]]; do let i++; done
test4: 26.69s
#!/bin/bash
i=0
while [ $i -le 4000000 ]; do let i++; done
test5: 12.79s
#!/bin/bash
export LC_ALL=C
for ((i=0; i != 4000000; i++)) {
:
}
Важной частью этого последнего является экспорт LC_ALL = C. Я обнаружил, что многие операции bash в конечном итоге выполняются значительно быстрее, если это используется, в частности, любая функция регулярного выражения. Он также показывает недокументированный синтаксис для использования {} и: в качестве запрета.