Что-то не так с моим скриптом или Bash намного медленнее Python?

Обратите внимание, что ваш цикл немного избыточен, 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
29
13.08.2016, 14:01
8 ответов

Это известная ошибка в bash; см. справочную страницу и выполните поиск по запросу "ОШИБКИ":

 ОШИБКИ 
Он слишком большой и слишком медленный. 
 

;)


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

Наиболее уместные выдержки:

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

...

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

...

Как было сказано ранее, выполнение одной команды имеет определенную стоимость. Огромные затраты, если эта команда не встроена, но даже если они встроены, стоимость велика.

И оболочки не предназначены для такой работы, они не претендуют на то, чтобы быть высокопроизводительными языками программирования. Это не так, это просто интерпретаторы командной строки. Итак, на этом фронте была проведена небольшая оптимизация.


Не используйте большие циклы в сценариях оболочки.

20
27.01.2020, 19:38

Циклы оболочки медленные, а цикл 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

( Подробности:

  • ЦП: Intel (R) Core (TM) i5 CPU M 430 @ 2,27 ГГц
  • ksh: версия sh (исследования AT&T) 93u + 2012-08-01
  • bash: GNU bash, версия 4.3.11 (1) -release (x86_64-pc-linux-gnu)
  • zsh : zsh 5.2 (x86_64-unknown-linux-gnu)
  • тире: 0.5.7-4ubuntu1

)

Результаты (сокращенные) (время на итерацию):

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 раза быстрее, чем , в то время как [ -циклы в той же оболочке.

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

55
27.01.2020, 19:38

Оболочка эффективна, если вы используете ее для того, для чего она была разработана (хотя эффективность - это редко то, что вы ищете в оболочке).

Оболочка - это интерпретатор командной строки, он предназначен для выполнения команд и их совместной работы над задачей.

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

10
27.01.2020, 19:38

Я заметил резкое отличие в 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

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

-3
27.01.2020, 19:38

Все в порядке (кроме ваших ожиданий), поскольку python действительно довольно быстр для некомпилированного языка, см. https://wiki.python.org/moin/PythonSpeed

3
27.01.2020, 19:38

Ответ: Bash намного медленнее Python.

Один небольшой пример - в сообщении в блоге Производительность нескольких языков .

7
27.01.2020, 19:38

Помимо комментариев, вы могли бы оптимизировать код немного, например,

#!/bin/bash
for (( i = 0; i <= 1000000000; i++ ))
do
: # null command
done

Этот код должен занимать немного меньше времени.

Но, очевидно, недостаточно быстро, чтобы быть действительно пригодным для использования.

2
27.01.2020, 19:38

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

Тест 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 в конечном итоге выполняются значительно быстрее, если это используется, в частности, любая функция регулярного выражения. Он также показывает недокументированный синтаксис для использования {} и: в качестве запрета.

18
27.01.2020, 19:38

Теги

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