устранить подоболочки для более быстрого процесса?

Я читал, что скрипты, которые вызывают подоболочку, работают медленно, что объясняет, почему мой скрипт медленный.

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

mmode=1
modes[1,2]="9,12,18,19,20,30,43,44,45,46,47,48,49"
until [[ -z $kik ]];do
    ((++mloop))
    kik=$(echo  ${modes[$mmode,2]} | cut -d "," -f $mloop)
    
    filename=$(basename "$f")
    # is all these lines
    
    xcolorall=$((xcolorall+$storednr)
    # also triggering
    
    pros2=$(echo "100/$totpix*$xcolorall" | bc -l) 
    IFS='.' read -r pros5 pros6 <<< "$pros2"
    procenthittotal2=$pros5.${pros6:0:2}
            
    #subshells and if,
    # is it possible to circumvent it?  
    #and more of the same code..
done

обновлено: переменная pros2 вычисляет процент, сколько % xcolorall приходится на totpix, а переменная kik получает число из массива modes, информирующее цикл о том, какой цвет он должен считать в этом цикле. Я подозреваю, что это основные сборщики, можно ли сделать это без подоболочек?


person Adam Larsson    schedule 16.12.2020    source источник
comment
Это зависит от вашей оболочки. Похоже, это bash, который разветвит любой $(...), а ksh93, например, нет. См., например, unix.stackexchange.com/a/421028   -  person firmament    schedule 16.12.2020
comment
Суть в том, что bash в лучшем случае используется для вызова других инструментов с соответствующими входными данными. Если вы лучше опишете свои входные данные, возможно, кто-то сможет придумать подход, который не требует этого решения для цикла bash.   -  person liborm    schedule 16.12.2020
comment
Я с liborm: я не думаю, что проблема в подоболочке, но в цикле. Циклы в bash медленные, с подоболочками или без них. Часто есть способы обойтись без петель.   -  person Socowi    schedule 16.12.2020
comment
Примечание. $(( )) не создает подоболочку; это арифметическое выражение, анализируемое непосредственно в основной оболочке. С другой стороны, var=$(echo something | somecommand) создает три подпроцесса: подоболочку для управления каналом, еще одну для выполнения echo и еще одну для запуска somecommand. var=$(somecommand <<<"something") создает только два (предупреждение: <<< — это функция только для bash).   -  person Gordon Davisson    schedule 16.12.2020
comment
@Socowi В моей системе цикл из 10 000 итераций занимает 0,03 секунды без подоболочки и 7,66 секунды с ней. Это разница в 250 раз для одной вилки, а в этом вопросе их несколько (5-7).   -  person that other guy    schedule 16.12.2020
comment
определить slow (5 секунд? 4 минуты? 2,5 часа?) ... наверное зависит от того, из чего состоит #and more of the same code; $(...) (одинарные форзацы ( и )) являются дополнительными оболочками; filename=$(...), возможно, можно было бы заменить некоторыми расширениями параметров (будет зависеть от формата $f); вместо цикла until я бы, вероятно, использовал while/IFS/read для прямого анализа строки с разделителями-запятыми, тем самым исключив все вызовы подпроцесса для повторной установки kik   -  person markp-fuso    schedule 16.12.2020
comment
присваивания filename=$(...) / xcolorall=$((...)) / pros2=$(...) / loc0=$((..) выполняются для каждого прохода через цикл; но нет никаких указаний (в предоставленном фрагменте кода), что эти назначения меняются при каждом проходе через цикл; в исходном коде я бы убедился, что эти типы назначений (2 из которых выполняют вызовы подпроцессов) перемещаются вверх/перед циклом, если их значения никогда не меняются внутри цикла   -  person markp-fuso    schedule 16.12.2020
comment
Для некоторых вещей вам нужны внешние инструменты (например, bc, bash не может выполнять математические операции с плавающей запятой), но другие вещи (например, cut и basename) bash может делать. На самом деле вы не показываете цикл или данные, которые вы зацикливаете: добавьте больше деталей к своему вопросу.   -  person glenn jackman    schedule 17.12.2020
comment
обновлены вопросы с дополнительной информацией   -  person Adam Larsson    schedule 17.12.2020
comment
@markp-fuso Я новичок в написании сценариев, раньше не читал IFS/read ..   -  person Adam Larsson    schedule 17.12.2020
comment
@Socowi Существуют ли какие-либо другие способы выполнения циклов или просто писать одну и ту же функцию снова и снова, чтобы она просто обрабатывала скрипт, а затем вы могли бы использовать какое-то условие для пропуска, когда все числа были обработаны?   -  person Adam Larsson    schedule 17.12.2020
comment
@AdamLarsson Это всегда зависит от задачи. Повторение одной и той же функции вручную не поможет и только раздует ваш скрипт. Я имел в виду что-то вроде замены for i in "$@"; do echo "$i"; done на printf %s\\n "$@", чтобы дать очень упрощенный пример. Здесь, я боюсь, на самом деле нужна петля. Но если скорость имеет значение, реализуйте этот цикл на другом языке.   -  person Socowi    schedule 17.12.2020
comment
@Socowi, чтобы bash мог выполнять другие языки?   -  person Adam Larsson    schedule 17.12.2020
comment
Может быть, сделать шаг назад и объяснить, что вы на самом деле пытаетесь сделать. Вы считаете пиксели определенного цвета на изображении? Возможно, вы вообще используете не тот инструмент.   -  person Mark Setchell    schedule 17.12.2020
comment
@MarkSetchell Я определяю цвета на картах, и по ним подсчитываю, сколько там деревьев/воды и прочего. используя imagemagick для создания гистограммы и ее расчета.   -  person Adam Larsson    schedule 17.12.2020
comment
Если вы обновите свой вопрос или, может быть, запустите новый и покажете свои изображения и то, что вы пытаетесь сделать, я думаю, мы сможем дать вам НАМНОГО ЛУЧШЕЕ решение, чем разбор текстового вывода об изображениях...   -  person Mark Setchell    schedule 17.12.2020
comment
@MarkSetchell это карты размером 600x600 пикселей, и с помощью imagemagick я получаю список того, сколько пикселей каждого, а затем сравниваю список с теми цветами, которые я ищу в данный момент, и они указаны в массиве режимов.   -  person Adam Larsson    schedule 17.12.2020
comment
Задайте новый вопрос (они бесплатны, как и ответы) с прикрепленными изображениями и подходящим описанием.   -  person Mark Setchell    schedule 17.12.2020
comment
@MarkSetchell Я проведу кое-какой тест перед тем, как узнать больше о том, что я хочу знать .. и нет, вопросы не бесплатны, и если вы не получите голосов, вы больше не сможете задавать вопросы ..   -  person Adam Larsson    schedule 17.12.2020


Ответы (1)


Вы можете заменить все подоболочки и внешние команды, показанные в вашем вопросе, встроенными модулями bash.

  • kik=$(echo ${modes[$mmode,2]} | cut -d "," -f $mloop) можно заменить на
    mapfile -d, -t -s$((mloop-1)) -n1 kik <<< "${modes[$mmode,2]}".
    Если здесь константа $mmode, лучше заменить весь цикл на
    while IFS=, read -r kik; do ...; done <<< "${modes[$mmode,2]}".
  • filename=$(basename "$f") можно заменить на
    filename=${f##*/}, который работает в 100 раз быстрее, см. тест.
  • pros2=$(echo "100/$totpix*$xcolorall" | bc -l) можно заменить на
    (( pros2 = 100 * xcolorall / totpix )), если вам не нужны десятичные знаки, или на
    precision=2; (( pros = 10**precision * 100 * xcolorall / totpix )); printf -v pros "%0${precision}d" "$pros"; pros="${pros:0: -precision}.${pros: -precision}", если вам нужны 2 десятичных знака.
    Конечно, вы можете опустить последние команды (для поворота 12345 в 123.45), пока вам действительно не понадобится десятичное число.

Но если скорость действительно имеет значение, напишите сценарий на другом языке. Я думаю, что awk, perl или python были бы здесь хорошим совпадением.

person Socowi    schedule 17.12.2020
comment
Я только что протестировал ваши решения, и первое из них снизило время выполнения в цикле 40xloop с 8,9 до 3,5 с, я поражен, что подоболочки такие медленные. - person Adam Larsson; 18.12.2020
comment
Однако решение с точностью 2 не сработало... - person Adam Larsson; 18.12.2020
comment
Спасибо, что сообщили мне об этом. Не могли бы вы уточнить не сработало? С totpix=1200; xcolorall=750 я получаю 62.50, что даже точнее, чем 62.49999999999999999750, которое я получаю от bc. Может быть, xcolorall тоже является числом с плавающей запятой? Если это так, то вам придется применить тот же трюк и здесь. - person Socowi; 18.12.2020
comment
когда я работаю с set -e, команда '(( pros = 10**precision * 100 * xcolorall / totpix ))' завершается с ошибкой 1, и я не получаю от нее никаких результатов, даже если я изменяю pros к pros2 в вашем коде. xcolorall - это количество пикселей, а не плавающее. Я обновил вопрос своей полной функцией для расчета процента. - person Adam Larsson; 19.12.2020
comment
может ли это быть потому, что xcolorall начинается со значения 0?, так как я первый цикл xcolorall all еще не назначил ни одного пикселя? - person Adam Larsson; 19.12.2020
comment
set -e здесь единственная проблема. Я никогда не использую его из-за таких проблем. Если выражение внутри (( expr )) оценивается как 0, тогда код выхода равен 1. Это не ошибка, а возможность написать стиль C if (( expr )), но с set -e скрипт завершает работу. Вы можете удалить set -e или (если вы действительно хотите его сохранить) добавить || true после каждого (( expr )). - person Socowi; 19.12.2020
comment
Я ранее устанавливал +e непосредственно перед этим, и это не сработало. Однако при тестировании вашего кода он работает автономно, поэтому что-то еще мешает, я проверю. - person Adam Larsson; 19.12.2020
comment
Просто небольшое обновление, все работает в соответствии с вашим кодом, понятия не имею, почему сначала это не сработало, теперь спасибо вашему коду, изображение, которое заняло 11,7 секунды, теперь занимает всего 0,6 секунды! Я никогда не подозревал, что подоболочки где такие хогеры.. - person Adam Larsson; 20.12.2020
comment
Спасибо, что позволил мне сейчас. Я рад, что смог помочь. - person Socowi; 20.12.2020