
У меня есть скрипт, который просто запускает другой процесс в фоновом режиме, пытаясь контролировать максимальное количество запущенных процессов (в данном случае 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