многопроцессорность для цикла bash

У меня есть нетривиальный Bash-скрипт примерно следующего вида:

# Initialization

<generate_data> | while read line; do

    # Run tests and filters on line

    if [ "$tests_pass" ]; then
        echo "$filtered_line"
    fi

done | sort <sort_option> | <consume_data>

# Finalization

По сравнению с фильтром генератор потребляет минимальные ресурсы обработки, и, разумеется, операция сортировки не может начаться, пока не будут доступны все отфильтрованные данные. Таким образом, фильтр, представляющий собой каскад из нескольких циклов и условных выражений, изначально написанных в Bash, является узким местом обработки, и единственный процесс, выполняющий этот цикл, потребляет все ядро.

Полезной целью было бы распределить эту логику по нескольким дочерним процессам, каждый из которых запускает отдельные циклы фильтрации и каждый из которых, в свою очередь, потребляет блоки строк из генератора и каждый из которых создает выходные блоки, объединенные в операцию сортировки. Функциональность такого рода доступна с помощью таких инструментов, как GNU Parallel, но для их использования требуется вызов внешней команды для запуска в конвейере.

Доступен ли какой-либо удобный инструмент или функция, позволяющая распределять операции над сценарием между несколькими процессами, не нарушая общей структуры сценария? Я не знаю встроенной функции Bash, но она наверняка была бы полезна.


person epl    schedule 02.12.2019    source источник
comment
будет ли достаточно использовать xargs?   -  person gregory    schedule 02.12.2019
comment
unix.stackexchange.com/questions/103920/ показывает несколько примеров параллельной обработки циклов в bash. Надеюсь, это может быть вам полезно.   -  person gzh    schedule 02.12.2019
comment
Спасибо. Я прочитал эту тему (103920), но не обнаружил ничего, что привело бы меня к решению этой проблемы, как построено. У вас есть?   -  person epl    schedule 02.12.2019
comment
Вы уверены, что это не разветвление?   -  person that other guy    schedule 02.12.2019
comment
@gregory Это будет работать аналогично GNU parallel, который OP не нужен (хотя invoking an external command не так плох, как запуск цикла в bash). @epl Возможно, можно достаточно ускорить ваш фильтр, не прибегая к параллельным вычислениям. С минимальным входом и ожидаемым результатом кто-то может дать вам решение здесь.   -  person Socowi    schedule 02.12.2019
comment
@Socowi Я разрабатываю оптимизированную реализацию логики фильтра. Мне не нужна особая помощь в этой работе. Но выгода от такого улучшения, примененного сама по себе, намного меньше, чем от параллельного использования большего количества оборудования. Проблема с вызовом внешней команды заключается в отсутствии управляемости кода в отношении переноса логики фильтра в какую-либо команду, которую можно вызывать независимо.   -  person epl    schedule 02.12.2019
comment
such an improvement applied by itself is far inferior to that from utilizing more hardware in parallel Я бы на это не рассчитывал. Циклы в bash настолько медленные, что даже в параллельном режиме они часто не могут обогнать другие языки или даже специализированные инструменты. Пример. Чтобы сгенерировать числа от 1 до 4 000 000, я сравнил следующие подходы на четырехъядерный. Один цикл bash (16,1 с); четыре петли баш параллельно (5,2с); один awk-цикл (0,9 с); и seq (0,1 с). Обратите внимание, что здесь используются только встроенные циклы. Если вы неоднократно вызываете внешние программы, это еще хуже.   -  person Socowi    schedule 02.12.2019
comment
@Sucowi Я, конечно, согласен, что Bash медленный, но портирование выходит за узкие рамки этой темы.   -  person epl    schedule 02.12.2019
comment
Извините, я не хотел призывать вас использовать совершенно другой язык. Я просто хотел оптимизировать ваш код bash для фильтра. Часто вещи можно написать короче и эффективнее, используя правильные инструменты (bash) для работы. Вам нужно только знать, что такое правильные инструменты — это то, что мне так нравится в программировании на bash. Это как головоломка.   -  person Socowi    schedule 03.12.2019
comment
Как насчет использования Redis? Вы можете легко LPUSH добавить строки/блоки в список Redis и запустить несколько процессоров, которые BRPOP блокируют список, а LPUSH приводят к другому списку. Задания процессора могут выполняться на bash, Python или C++ на всех компьютерах в вашей сети.   -  person Mark Setchell    schedule 05.12.2019
comment
Пример Redis... stackoverflow.com/a/22220082/2836621   -  person Mark Setchell    schedule 05.12.2019
comment
Конечно, в целом существует бесчисленное множество подходов, но опять же, цель вопроса очень узко связана с сохранением существующей структуры кода при добавлении использования параллельных процессов. Обоснование этой темы состоит в том, чтобы понять ограничения и возможности, предлагаемые bash и связанными инструментами, а не в мозговом штурме общих стратегий для параллельной обработки. Спасибо.   -  person epl    schedule 07.12.2019
comment
@epl Я думаю, что будет легче ответить на ваш вопрос, если вы строго определите, что вы подразумеваете под bash и связанными с ним инструментами. Я думаю, что без строгого определения этого вы получите ответы, в том числе то, что ответчик считает bash и связанными с ним инструментами. Например. Я бы нашел GNU Parallel очень похожим инструментом - в основном он только имеет смысл при запуске из оболочки. Но я чувствую, что вы не включаете GNU Parallel в свое определение.   -  person Ole Tange    schedule 07.12.2019
comment
Вы уже экспериментировали с & и ждете $!? Я обычно сохраняю каждый результат в массиве, жду завершения всех PID, а затем запускаю процесс сортировки/окончания. Я напишу ответ с коротким примером позже сегодня, если хотите.   -  person Matthieu    schedule 07.12.2019
comment
@OleTange Достаточно честно. В первую очередь я имею в виду встроенные функции оболочки и исполняемые вызовы, которые, вероятно, будут доступны в среде *Nix и которые обычно используются для расширения сценариев оболочки за пределы собственных возможностей встроенных функций. GNU Parallel будет включен в качестве сопутствующего инструмента. Базы данных, очереди сообщений и специализированные инструменты вряд ли будут включены. Такие инструменты, как AWK, Perl и sed, могут быть включены, но переписывание блоков кода на таком языке, хотя и правдоподобно, не входило в цель вопроса, который скорее заключался в характеристике ограничений и возможностей Bash.   -  person epl    schedule 07.12.2019
comment
@epl Имеет смысл включать Perl только в том случае, если вы также разрешаете программы Perl: вы не можете использовать Perl, не написав программу Perl. GNU Parallel — это программа на Perl, и вы можете гарантировать, что она будет доступна для вашего сценария, включив ее в сценарий с параметром --embed. GNU Parallel активно тестируется на широком спектре платформ, и если он не работает на платформе *Nix, это считается ошибкой. Если | пока .. сделано | нельзя изменить на | параллель --pipe .. | поскольку это рассматривается как переписывание блока, то я думаю, что трудно дать вам правильный ответ, кроме нет, это невозможно сделать.   -  person Ole Tange    schedule 07.12.2019


Ответы (2)


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

Если это причина того, что вы не используете GNU Parallel, то это звучит так, как будто вы не знаете о parallel --embed.

--embed создан именно потому, что людям нужно иметь GNU Parallel в том же файле, что и остальной код.

[output from parallel --embed]

myfilter() {
    while read line; do
      # Run tests and filters on line
      if [ "$tests_pass" ]; then
        echo "$filtered_line"
      fi
    done
}   
export -f myfilter

<generate_data> | parallel --pipe myfilter | sort <sort_option> | <consume_data>

Полученный сценарий будет работать, даже если GNU Parallel не установлен.

person Ole Tange    schedule 05.12.2019
comment
Я не знал о --embed, который кажется очень новым и не включен даже в недавние дистрибутивы. Но если целью опции является создание скриптов, которые запускаются без зависимости, то не будет ли текущий вопрос совершенно неуместным? - person epl; 07.12.2019

Полезной целью было бы распределить эту логику по нескольким дочерним процессам, каждый из которых запускает отдельные циклы фильтрации и каждый из которых, в свою очередь, потребляет блоки строк из генератора и каждый из которых создает выходные блоки, объединенные в операцию сортировки. Функциональность такого рода доступна с помощью таких инструментов, как GNU Parallel, но для их использования требуется вызов внешней команды для запуска в конвейере.

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

Любой удобный инструмент...

Без вашего определения «удобный инструмент» ответить невозможно. Лично мне parallel --pipe cmd было бы удобно, но, возможно, это не соответствует вашему определению.

... или доступная функция, которая позволяет распределять операции над сценарием между несколькими процессами, не нарушая общей структуры сценария? Я не знаю встроенной функции Bash, но она наверняка была бы полезна.

Встроенного Bash нет. Это основная причина, по которой GNU Parallel имеет опцию --pipe.

Использование | parallel --pipe myfilter |, кажется, хорошо вписывается в общую структуру скрипта.

person Ole Tange    schedule 07.12.2019
comment
Конечно, я не возражаю против вызова исполняемых процессов, таких как sort. Цель процитированного вами комментария состояла в том, чтобы привлечь внимание к воспринимаемому ограничению, заключающемуся в том, что команда, передаваемая в Parallel, должна быть внешним исполняемым файлом, а не частью текущего скрипта. Вы бросаете вызов этому восприятию? - person epl; 07.12.2019
comment
@epl Поскольку я делаю именно это в другом ответе (а именно, передавая ему функцию, определенную в том же скрипте, а не исполняемый файл), я делаю вызов этому восприятию. Вы даже можете дать ему псевдоним, если используете env_parallel. - person Ole Tange; 07.12.2019