Исходный код.

Исходный код.

У меня есть скрипт, который просто запускает другой процесс в фоновом режиме, пытаясь контролировать максимальное количество запущенных процессов (в данном случае 300).

Первоначально он выполняет скрипты примерно за 1-2 миллисекунды, но после нескольких часов работы он в конечном итоге замедляется по линейному наклону до 200 мс - 350 мс execs. Я использую массив для поддержания PID#, но также снимаю ключ, чтобы уменьшить размер таблицы, но у меня есть чувство, что это виновник.

#!/bin/bash

threads=()
threadcounter=0

crd=0;

while true; 
do
        threadcounter=${#threads[@]}
        crdcounter=${#crds[@]}

        if [ "$threadcounter" -lt 300 ]
        then
                s1=$(($(date +%s%N)/1000000))
                pidf=$(/opt/remi/php/root/usr/bin/php cli.php initformula $crd >> /tmp/logger) &
                pidfid=$!
                s2=$(($(date +%s%N)/1000000))
                echo "Init " $crd $(expr $s2 - $s1 ) "ms"
                threads[$pidfid]+=$pidfid
        else
            for pid in "${!threads[@]}"; do 
                if [ ! -d "/proc/${pid}" ]
                then
                    unset threads[$pid]
                fi
            done;
        fi;

        if [ "$crd" -gt 9999999 ]
        then
            echo "completed all";
            exit;
        fi;

        crd=$(expr $crd + 1)
done;

решение1

Исходный код.

Когда вы начинаете, вы просто запускаете 300 копий cli.php. Это занимает около 1200 процессов, потому что вы хотите измерить время, необходимое для запуска.

Затем вы циклически изменяете переменную crdот 300 до 9999999.

  • Если оболочка полагает, что в threadsмассиве есть свободные слоты, она начнет новый, cli.phpиспользуя 4 процесса.

  • В противном случае вы зацикливаете около 300 процессов, заставляя
    ядро ​​заполнить /procвиртуальную файловую систему и проверяя,
    существует ли каталог. Любые отсутствующие каталоги приведут к
    удалению записей из threadsмассива.

У вас есть неиспользуемый массив с именем crds.

Поскольку после начальных 300 каждый цикл переменной crd будет создавать 1 новую копию, cli.phpесли в таблице процессов есть свободные слоты, но может удалить до 300, если таблица заполнена, в конце выполнения мы знали только, что cli.phpбыло запущено от 300 до примерно 9 967 000 процессов, причем число определялось скоростью работы вашего компьютера, временем cli.phpвыполнения и нагрузкой на компьютер. 6 порядков величины — это слишком много для оптимизации!

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

Улучшения

Один из способов ускорить это — использовать ! kill -0 $pidвместо [ ! -d "/proc/${pid}" ]- kill -0ничего не убивает, но возвращает ошибку, если процесс не существует. kill— встроенная оболочка (как есть [), но объем работы, которую должно выполнить ядро, меньше. Это будет наиболее эффективно, если большую часть времени в массиве нет свободных слотов threads.

Второе улучшение заменит вызов внешней программы exprна использование встроенной $(( ... ))арифметики, тем самым сократив накладные расходы на запуск копии cli.php. Это наиболее эффективно, если большую часть времени в массиве есть свободные слоты labels.

Для проведения более глубокого анализа нам необходимо знать приблизительное время, cli.phpнеобходимое для выполнения, и количество выполненных запусков.

Как BUGSговорится в разделе руководства по bash It's too big and too slow., вполне возможно, что есть возможности для улучшения реализации массивов в bash.

Альтернативные реализации

делать

В комментариях предлагается использовать xargsили parallel. Я часто предпочитаю использовать make. Первое, что нужно сделать, это определить, сколько копий cli.phpнужно. Затем простое, Makefileнапример,

%:
\t/opt/remi/php/root/usr/bin/php cli.php initformula $@

где \t — символ табуляции. (Эта простая версия предполагает, что у вас нет файлов с числовыми именами в диапазоне от 0 до 9999999). Затем вызовите make как

make -O -j 300 $(seq 0 9999999) > /tmp/logger

если вы хотите полные 10 000 000 вызовов cli.php. Причины, по которым я предпочитаю makeвключить xargsотсутствие необходимости предпринимать чрезмерные шаги для прерывания обработки, если cli.php возвращает ошибки.

xargs

Для xargsрешения попробуйте

seq 0 9999999 | xargs -n 1 -P 300 /opt/remi/php/root/usr/bin/php cli.php initformula > /tmp/logger

что проще.

Баш

Однако решение Bash, которое использует wait -nfи вообще не беспокоится об отслеживании PID, может быть больше по вкусу OP. Оно запускает начальные 300 процессов, затем, обнаружив завершение одного из них, запускает другой. После запуска 10 000 000-го он делает финальное ожидание, чтобы все задания завершились. Не совсем тот же алгоритм, но очень близко.

#!/bin/bash
for(crd=0;crd<300;crd++); do
    /opt/remi/php/root/usr/bin/php cli.php initformula $crd & 
done > /tmp/logger
for(;crd<=9999999;crd++); do
    wait -fn
    /opt/remi/php/root/usr/bin/php cli.php initformula $crd &
done >> /tmp/logger
wait

Связанный контент