2010-12-31 5 views
4

24 개의 코어가있는 서버에서 Linux [2]에서 실행되는 JVM [1]은 하나의 커다란 힙 (최대 240GB이지만,이 실행 단계의 대부분은 20-40GB 범위). 우리는 외부 실행 파일 &에 의해 처리되어야하는 수만 개의 객체를 가지고 있으며, 그 실행 파일에 의해 생성 된 데이터를 다시 JVM으로로드합니다. 각 실행 파일은 약 0.5 메가 바이트의 데이터를 디스크에서 생성합니다.이 데이터는 프로세스 완료 후 바로 읽을 때 더 커집니다.Java에서 프로세스 생성 속도 저하?

첫 번째 구현에서는 각 실행 파일이 단일 개체 만 처리하도록했습니다. 이것은 우리가 객체를 가졌던 것보다 두 배나 많은 실행 파일을 생성하는 것과 관련이 있습니다 (실행 파일을 호출 한 쉘 스크립트라고 부르기 때문에). CPU 사용률은 높지만 반드시 100 %는 아니지만 천천히 악화됩니다. 우리가 무엇이 일어나고 있는지 측정하기 시작하면서 프로세스 생성 시간 [3]이 지속적으로 느려짐을 발견했습니다. 초초부터 시작하여 결국 1 분 이상이 걸릴 것입니다. 실행 파일에 의해 수행되는 실제 처리는 보통 10 초 이내에 완료됩니다.

다음으로 생성 된 프로세스의 수를 줄이기 위해 처리 할 개체 목록을 취하도록 실행 파일을 변경했습니다. 배치 크기가 수백 (현재 샘플 크기의 1 %)이면 프로세스 생성 시간은 약 2 초에서 시작하여 &은 약 5-6 초로 증가합니다.

기본적으로 실행이 계속되면서 이러한 프로세스를 만드는 데 오랜 시간이 걸리는 이유는 무엇입니까?

[1] 오라클 JDK 1.6.0_22
[2] 레드햇 엔터프라이즈 리눅스 고급 플랫폼 5.3은 ProcessBuilder를 객체의 리눅스 커널 2.6.18-194.26.1.el5 # 1 SMP
[3] 창조, 오류 스트림을 리디렉션하고 시작합니다.

+0

@ oconnor0 : omg ... 같은 문제가 있습니다. 리소스를 공개하는 것을 잊어 버리거나 어딘가에 버그가 있습니까? ** 많은 외부 프로세스를 인스턴스화하고 시간이 지남에 따라 느려지고 느려진다는 것을 알았습니다. 오류 (있는 경우)를 발견하면 여기에 다시 신고 해 주시겠습니까? +1하고 즐겨 찾기 ... (나를 위해 btw 이것은 OS X 10.4, 10.5 및 10.6에서 발생). – SyntaxT3rr0r

+1

프로세스에서 생성 한 Input/OutputStream을 닫고 있는지 확인할 수 있습니까? –

+0

@Mike Q : 제 경우에는 외부 프로세스가 모든 단일 출력을 파일 (stdout 및 stderr 포함)로 리디렉션하므로 아무 것도 생성되지 않습니다. – SyntaxT3rr0r

답변

3

Java에서 fork/exec 시스템 호출을 사용하여 하위 프로세스를 생성하는 경우 fork/exec 문제가 발생할 가능성이 있습니다.

일반적으로 fork()는 거의 효과가 없으므로 fork/exec는 매우 효율적입니다. 모든 페이지는 copy-on-write입니다. 매우 큰 프로세스 (즉,매핑 된 페이지가있는 페이지 테이블)은 페이지 테이블 자체가 생성하는 데 비교적 오랜 시간이 걸리기 때문에 - 당연히 exec를 호출 할 때 파기해야합니다.

많은 양의 힙을 사용하고 있기 때문에 이것이 영향을 줄 수 있습니다. 더 많은 페이지를 매핑하면 더 악화 될 수 있으며 이는 점진적인 속도 저하의 원인이 될 수 있습니다.

고려 중 하나가이 libc의/수확 다른 사람을 만들기위한 책임이 하나의 서브 프로세스를 사용

  • 에 포크/간부에 의해 구현되어 있지 않은 경우의 posix_spawn를 사용

    • ; 이것을 한 번 스폰하고 IPC (파이프 등)를 사용하여 무엇을해야하는지 알려줍니다.

    주의 : 이것은 모두 추측입니다. 이것이 사실인지 알아보기 위해 몇 가지 실험을해야 할 것입니다.

  • +0

    아직 확실하지 않지만, 당신이 옳다고 생각합니다. 나는 24 개의 프로그램을 동시에 스폰하기위한 간단한 테스트 프로그램을 작성하고 힙 크기를 늘리면서 프로세스 생성 시간이 증가하는 것을 지켜 보았습니다. https://github.com/axiak/java_posix_spawn에서 몇 가지 수정을해야만하는 라이브러리를 찾았습니다.하지만 이제는 대규모 힙을 사용하는 경우에도 프로세스 생성 시간이 몇 배 빨라졌습니다. 나는 라이브러리가 얼마나 유용한지를 알아야 할 것입니다. 만약 다른 것이 없다면, 그 답이 발견 된 것처럼 보일 것입니다. – oconnor0

    +0

    posix_spawn이 fork() 및 exec()로 구현되었는지 확인 했습니까? 아마도 그것은 vfork()와 exec에 의해 구현 될 것인데, 이는 페이지 테이블 복사를 피할 수 있습니다 (부모가 exec까지 일시 중단 될 때)? posix_spawn이 내부적으로 같은 일을한다면, 그것은 무의미합니다. – MarkR

    +0

    글쎄, 리눅스에서 Oracle JDK가하는 일은 프로세스 생성 속도를 늦추고 있지만이 버전은 그렇지 않습니다. – oconnor0

    1

    대부분 리소스가 부족합니다. 이러한 프로세스를 생성 할 때 디스크가 더 많이 사용됩니까? 코어보다 프로세스가 적도록 보장합니까? 컨텍스트 스위치를 최소화하려면로드 평균이 24 미만입니까?

    CPU 사용량이 감소하면 입출력 (디스크/네트워크) 경합이 발생할 가능성이 높습니다. 즉, 프로세스가 데이터를 빠르게 쓰거나 쓸 수 없으므로 프로세스를 바쁘게 유지할 수 없습니다. 24 개의 코어가 있다면 얼마나 많은 디스크가 있습니까?

    CPU 당 하나의 프로세스가 있다고 제안합니다. (4 가지 경우를 생각해보십시오.) 각 JVM에 시스템을 과부하하지 않고 모든 코어를 사용하도록 동시에 실행할 수있는 6 가지 태스크를 부여하십시오.

    +0

    우리는 약 30 개의 프로세스로 실행 중이 었으므로 24로 축소하여 차이가 있는지 확인했습니다. JVM을 포함하여 우리의 평균 부하는 약 24.5입니다. 이상한 일 중 하나는 각 프로세스가 성장하는 데 걸리는 시간을보고 있다는 것입니다. – oconnor0

    +0

    OP와 동일 : 문제는 프로세스를 생성하는 데 걸리는 시간이 매번 증가한다는 것입니다. – SyntaxT3rr0r

    +0

    또한 프로세스 당 작업을 일괄 처리 할 때 모든 프로세서를 사용할 수있는 프로세스가 더 이상 없을 때까지 실행이 끝날 때까지 전체 CPU 사용률을 얻습니다. – oconnor0

    0

    나는 Peter에게 가장 동의합니다. 귀하는 IO 병목 현상을 겪고있을 가능성이 큽니다. 일단 당신이 처리 할 수있는 OS는 사소한 작업을 위해 더 열심히 일해야하며 따라서 지수 성능 저하를 가져야합니다.

    그래서 '솔루션'은 '소비자'프로세스를 생성 할 수 있으며 일부만 초기화 할 수 있습니다. Peter는 CPU 당 하나 이상을 제안했습니다. 그런 다음 IPC를 사용하여 이러한 개체를 소비자 프로세스에 '전송'하십시오.

    귀하의 '소비자'프로세스는 하위 프로세스 생성을 관리해야합니다. 내가 처리 할 수있는 실행 파일은 당신이 접근 할 수 없다고 가정하고, 이렇게하면 실행 파일이 너무 많아서 OS가 혼란스럽지 않고 "작업"이 "결국"완료 될 것입니다.

    1

    데이터를 대기열로부터 꺼내어 각 이벤트, 특히 엄청난 힙을 가진 호스트 JVM에서 끊임없이 새로운 프로세스를 포크하는 긴 수명 프로세스 세트를 사용하는 것이 훨씬 더 나을 것입니다.

    240GB 이미지를 포킹하는 것은 무료가 아니며, 1 초 동안에도 많은 양의 가상 리소스를 소비합니다. 운영체제는 새로운 프로세스가 얼마나 오래인지 알지 못하기 때문에 전체 프로세스가 오래 살아있는 것처럼 준비해야하므로 exec 호출로이를 제거하기 전에 모든 240GB의 가상 복제본을 설정합니다.

    일부 대기열 메커니즘을 통해 개체를 종료 할 수있는 수명이 긴 프로세스가있는 경우 (Java 및 C 등 많은 개체가있을 수 있음) 포크 프로세스의 일부 부담을 덜어줍니다 .

    JVM에서 외부 프로그램으로 데이터를 전송하는 방법을 모르겠습니다. 그러나 외부 프로그램이 stdin/stdout에서 작동 할 수 있다면 (유닉스를 사용한다고 가정 할 때) inetd를 활용할 수 있습니다. 여기서 프로세스에 대한 inetd 구성 파일에 간단한 항목을 작성하고 포트를 지정하십시오. 그런 다음 소켓을 열어서 데이터를 입력 한 다음 소켓에서 다시 읽습니다. Inetd는 네트워킹 세부 정보를 처리하고 stdin 및 stdout을 사용하여 프로그램이 작동합니다. 네트워크에 개방 소켓이있어 배포시 안전 할 수도 있고 안전하지 않을 수도 있습니다. 그러나 레거시 코드를 네트워크 서비스를 통해 실행하도록 설정하는 것은 매우 간단합니다.

    #!/bin/sh 
    infile=/tmp/$$.in 
    outfile=/tmp/$$.out 
    
    cat > $infile 
    /usr/local/bin/process -input $infile -output $outfile 
    cat $outfile 
    rm $infile $outfile 
    

    그것은 거래 엄청나게하도록 설계 지구상에서 최고 성능의 서버 아니지만, 그것은 훨씬 빨리 반복을 통해 2백40기가바이트을 분기하고보다 확실입니다 :

    는이 같은 간단한 래퍼를 사용할 수 있습니다.

    관련 문제