원본 코드.

원본 코드.

백그라운드에서 다른 프로세스를 실행하여 실행 중인 최대 프로세스 수(이 경우 300개)를 제어하는 ​​스크립트가 있습니다.

처음에는 스크립트를 대략적으로 실행합니다. 1~2ms이지만 몇 시간 동안 실행한 후에는 결국 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

원본 코드.

시작할 때 cli.php시작하는 데 걸리는 시간을 측정하려고 하므로 300개의 복사본만 시작하면 됩니다. 이는 약 1200개의 프로세스여야 합니다.

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

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

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

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

초기 300개 이후 cli.php프로세스 테이블에 사용 가능한 슬롯이 있으면 crd 변수의 각 루프는 1개의 새 복사본을 생성하지만 테이블이 가득 차면 최대 300개의 복사본을 삭제할 수 있으므로 실행이 끝나면 단지 300개에서 약 9,967,000개의 cli.php프로세스가 시작된 것으로 알려져 있으며 , 프로세스 수는 컴퓨터 속도, cli.php실행 시간, 컴퓨터 부하에 따라 달라집니다. 최적화할 수 있는 크기는 6개입니다!

경험상 최신 시스템에서는 하나의 프로세스를 실행하는 데 하나의 코어에서 1밀리초가 걸리므로 초기 실행 속도에서는 문제가 없습니다. 새로운 프로세스를 시작하기 위해 사용 가능한 코어가 부족해지면 시작 속도가 크게 바뀔 것으로 예상됩니다.

개선하다

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

expr두 번째 개선 사항은 외부 프로그램 호출 대신 내장된 연산을 사용하여 $(( ... ))시작 시간을 줄이는 것입니다 cli.php. 이는 대부분의 시간 동안 배열에 여유 슬롯이 있는 경우에 가장 효율적입니다 labels.

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

BUGSBash 매뉴얼 섹션에서 언급했듯이 Bash It's too big and too slow.의 배열 구현에는 확실히 개선의 여지가 있습니다.

대체 구현

만들다

의견에 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해결책을 찾으 려면

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

이것은 더 간단합니다.

불다

그러나 wait -nfPID 추적을 전혀 걱정하지 않고 사용하는 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

관련 정보