원본 코드.

원본 코드.

실행 중인 최대 프로세스 수(이 경우 300개)를 제어하려고 시도하는 다른 프로세스를 백그라운드로 실행하는 스크립트를 생각해 냈습니다.

처음에는 약에서 스크립트를 실행합니다. 1~2밀리초이지만 몇 시간 실행한 후에는 결국 선형 경사로 200ms~350ms 실행 속도로 느려집니다. 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개의 프로세스가 필요합니다.

그런 다음 변수를 crd300에서 9999999까지 반복합니다.

  • 쉘이 어레이에 여유 슬롯이 있다고 생각하면 4개의 프로세스를 사용하여 threads새 슬롯을 시작합니다 .cli.php


  • 그렇지 않으면 커널이 가상 파일 시스템을 채우도록 /proc하고 디렉토리가 존재하는지 테스트하는 약 300개 이상의 프로세스를 반복합니다
    . 누락된 디렉토리가 있으면 항목이 어레이
    에서 삭제 됩니다 threads.

라는 사용되지 않은 배열이 있습니다 crds.

처음 300개 이후 crd 변수의 각 루프는 프로세스 테이블에 사용 가능한 슬롯이 있는 경우 1개의 새 복사본을 생성 cli.php하지만 테이블이 가득 차면 최대 300개까지 제거할 수 있기 때문에 실행이 끝날 때 우리는 300개에서 약 9,967,000개 사이의 cli.php프로세스가 시작되었으며, 숫자는 시스템 속도, cli.php실행 시간 및 시스템 로드에 따라 결정됩니다. 6배의 크기는 최적화해야 할 것이 많습니다!

경험상 최신 시스템에서는 프로세스 1개를 시작하는 데 코어 하나에서 1ms가 걸리므로 초기 실행 속도에서는 나쁘지 않습니다. 새로운 프로세스를 시작하기 위한 여유 코어가 부족해지면 실행 속도가 크게 달라질 것으로 예상됩니다.

개량

속도를 높이는 한 가지 방법은 - 아무것도 죽이지 않지만 프로세스가 존재하지 않으면 오류 반환을 제공하는 ! kill -0 $pid대신 사용하는 것입니다. 는 쉘에 내장되어 있지만(있는 그대로 ) 커널이 수행해야 하는 작업의 양은 더 적습니다. 이는 대부분의 경우 어레이에 사용 가능한 슬롯이 없는 경우 가장 효과적입니다 .[ ! -d "/proc/${pid}" ]kill -0kill[threads

두 번째 개선 사항은 외부 프로그램에 대한 호출을 내장된 산술 연산을 expr사용하여 대체하여 . 이는 대부분의 시간 동안 어레이에 여유 슬롯이 있는 경우 가장 효과적입니다 .$(( ... ))cli.phplabels

cli.php더 많은 분석을 수행하려면 실행하는 데 걸리는 대략적인 시간과 실행 횟수를 알아야 합니다 .

BUGSBash 매뉴얼의 섹션에서 알 수 있듯이 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 호출을 원하는 경우. cli.php가 오류를 반환하는 경우 처리를 중단하기 위해 과도한 조치를 취할 필요가 없다는 점을 포함 make하고 싶은 이유 .xargs

xargs

xargs해결책을 찾으 려면

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

더 간단합니다.

세게 때리다

wait -nf그러나 PID 추적을 전혀 걱정하지 않고 사용하는 Bash 솔루션은 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

관련 정보