2013-08-21 3 views
0

"1 블록 x 32 스레드"구성으로 시작하려는 커널이 있습니다. 병렬성을 높이려면 "1 블록 x 32 스레드"보다 더 큰 "작업 패키지"를 실행하는 대신 여러 스트림을 시작하고 싶습니다. 데이터가 네트워크에서 오는 프로그램에서 GPU를 사용하고 싶습니다. 더 큰 "작업 패키지"가 나올 때까지 기다릴 필요가 없습니다. 실제 코드를 훨씬 더 복잡하지만 (15 개 CPU의 스레드가 GPU를 사용) 간단하게 할여러 스레드 (스레드/블록 대신) 병렬 실행

Thread(i=0..14) { 
    - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)] 
    - run kernel(stream i) 
    - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)] 
} 

: 같은 코드입니다.

코드는 작동하지만 스트림이 예상대로 동시에 실행되지 않습니다. GTX 480에는 15 개의 SM이 있으며 각 SM에는 32 개의 쉐이더 프로세서가 있습니다. 커널을 15 번 시작하면 15 개의 스트림이 모두 병렬로 실행되지만, 그렇지는 않습니다. 저는 Nvidia Visual Profiler를 사용했으며 병렬로 실행되는 최대 5 개의 스트림이 있습니다. 종종 하나의 스트림 만 실행됩니다. 성능이 정말 안좋아.

"64 블록 x 1024 스레드"구성으로 최상의 결과를 얻었습니다. 대신 "32 블록 x 1024 스레드"구성을 사용하지만 두 개의 스트림이 서로 차례로 실행되고 성능이 떨어지면 Cuda Toolkit 5.5와 Ubuntu 12.04를 사용하고 있습니다.

누군가 이것이 왜 그런지 설명하고 배경 정보를 줄 수 있습니까? 더 새로운 GPU에서 더 잘 작동해야합니까? 데이터를 버퍼링하지 않으려는 시간에 비판적인 애플리케이션에서 GPU를 사용하는 가장 좋은 방법은 무엇입니까? 아마도 이것은 가능하지 않지만 해결책을 찾도록하는 기술을 찾고 있습니다.

뉴스

:

는 좀 더 많은 연구를했다. 문제는 마지막 cudaMemcpyAsync (..) (GPU-> 호스트 복사) 호출입니다. 제거하면 모든 스트림이 동시에 실행됩니다. 나는 그 문제가 슬라이드 21의 http://on-demand.gputechconf.com/gtc-express/2011/presentations/StreamsAndConcurrencyWebinar.pdf에 삽화되어 있다고 생각한다. 그들은 페르미 (Fermi)에 2 개의 복사 대기열이 있다고 말하고 있지만 이것은 테슬라와 쿼드로 카드에 대해서만 유효하다. 문제는 GTX 480에 하나의 복사 대기열이 있고 모든 복사 명령 (호스트 -> GPU 및 GPU -> 호스트)이이 대기열에 들어 있다고 생각합니다. 모든 것이 non-blocking이며 첫 번째 스레드의 GPU-> 호스트 memcopy는 다른 스레드의 호스트 -> GPU memcopy 호출을 차단합니다. 여기에 일부 관찰 :

Thread(i=0..14) { 
    - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)] 
    - run kernel(stream i) 
} 

-> 작품은 : 스트림을 동시에

Thread(i=0..14) { 
    - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)] 
    - run kernel(stream i) 
    - sleep(10) 
    - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)] 
} 

실행 -> 동작 : 스트림을 동시에 실행

Thread(i=0..14) { 
    - copy data Host -> GPU [cudaMemcpyAsync(.., stream i)] 
    - run kernel(stream i) 
    - cudaStreamSynchronize(stream i) 
    - copy data GPU -> Host [cudaMemcpyAsync(.., stream i)] 
} 

-> 작동하지 않습니다! 어쩌면 cudaStreamSynchronize가 복사 대기 행렬에 놓여 있습니까?

누군가이 문제에 대한 해결책을 알고 있습니까? 블로킹 커널 호출과 같은 것이 멋질 것입니다. 마지막 cudaMemcpyAsync() (GPU-> 장치)는 커널이 끝나면 호출되어야합니다.

Edit2가 : 첫 번째 스트림이 시작

Stream1: 
------------ 
HostToGPU1 
kernel1 
GPUToHost1 

Stream2: 
------------ 
HostToGPU2 
kernel2 
GPUToHost2 

: 우리는 2 개 스트림을 간단하게하기 위해 : 여기 예를 들어 내 문제를 명확히한다. HostToGPU1이 실행되면 kernel1이 시작되고 GPUToHost1이 호출됩니다. kernel1이 실행 중이기 때문에 GPUToHost1이 차단됩니다. 그 동안 Stream2가 시작됩니다.HostToGPU2가 호출되면 Cuda는 큐에 넣지 만 커널 1이 완료 될 때까지 GPUToHost1을 차단하기 때문에 실행할 수 없습니다. 현재로서는 데이터 전송이 없습니다. Cuda는 GPUToHost1을 기다립니다. 그래서 제 생각은 kernel1이 끝났을 때 GPUToHost1을 호출하는 것이 었습니다. GPUToHost1은 커널이 끝났을 때 호출되기 때문에이 연결은 절전 (..)과 함께 작동하는 이유입니다. CPU 스레드를 자동으로 차단하는 커널 실행은 멋지다. GPUToHost1이 대기열에서 차단되지 않습니다. (다른 경우에는 데이터 전송이 없지만 데이터 전송에는 시간이 많이 걸리지 않습니다.)

+0

Windows 또는 Linux에 있습니까? –

+0

우분투 12.04를 사용하고 있습니다. – user4811

+1

CUDA [concurrent kernels sample] (http://docs.nvidia.com/cuda/cuda-samples/index.html#concurrent-kernels)을 실행하면 어떤 결과가 나타 납니까? (그것은/usr/local/cuda/samples/6_Advanced/concurrentKernels' 시스템에서 이미 사용 가능해야합니다.) 테스트의 결과로 질문을 편집 할 수 있습니까? –

답변

2

동시 커널 실행은 Linux에서 가장 쉽게 볼 수 있습니다.

좋은 예와 쉬운 테스트를 위해 concurrent kernels sample을 참조하십시오.

는 커널 사이

좋은 동시성은 일반적으로 몇 가지가 필요합니다

  • 동시 커널을 지원하는 장치, 그래서 CC 2.0 이상 장치 블록 및 기타 자원의 수의 측면에서 충분히 작은
  • 커널 사용법 (레지스터, 공유 메모리)을 사용하여 여러 커널을 실제로 실행할 수 있습니다. 더 큰 자원 요구 사항을 가진 커널은 일반적으로 연속적으로 실행되는 것으로 관찰됩니다. 이는 예상되는 동작입니다. 스트림
  • 적절한 사용이 병행 또한

있도록 동시 커널은 자주 복사/계산 오버랩을 의미한다. 복사/계산 중복이 작동하려면 충분한 복사 엔진이있는 GPU를 사용해야합니다.

  • 일부 GPU에는 엔진이 하나 있고, 일부에는 2가 있습니다. GPU에 엔진이 하나만있는 경우 커널 실행으로 한 번의 복사 작업 (즉, 한 방향)을 오버랩 할 수 있습니다. GeForce GPU에 2 개의 복사 엔진이 있으면 커널 실행으로 복사의 양방향을 겹칠 수 있습니다.
  • GPU 전역 메모리로 복사하거나 GPU 전역 메모리에서 복사 할 모든 데이터에 대해 고정 된 (호스트) 메모리를 사용합니다. 중복하려는 복사 작업의 대상이됩니다.
  • 스트림을 올바르게 사용하십시오 그리고 작은 32x1024 커널을 동시에 실행하지 않는 당신의 관찰에 관한 관련 API 호출에 필요한 비동기 버전 (예 : cudaMemcpyAsync

,이 가능성이 많은 중복을 방지 자원 문제 (블록, 레지스터, 공유 메모리)입니다. 첫 번째 커널에 GPU 실행 리소스를 차지할만큼 충분한 블록이있는 경우 첫 번째 커널이 fini가 될 때까지 추가 커널이 실행을 시작할 것으로 기대하는 것은 바람직하지 않습니다 창고 또는 대부분 마쳤다.

편집 : 아래의 수정 사항 및 추가 의견에 대한 회신.

그렇습니다. GTX480에는 단 하나의 복사본 "대기열"이 있습니다 (필자는이 답변을 명시 적으로 언급했지만 사본은 "엔진"이라고 함). 하나만 얻을 수 있습니다 cudaMemcpy ... 주어진 시간에 작업이 실행되기 때문에 한 방향 (H2D 또는 D2H) 만 실제로 주어진 시간에 데이터를 이동할 수 있으며 하나만 표시됩니다 cudaMemcpy ... 주어진 커널과 연산이 중복됩니다. 그리고 cudaStreamSynchronize은 스트림이 이전에 해당 스트림에 발행 된 ALL CUDA 작업이 완료 될 때까지 대기하게합니다.

마지막 예에서 가지고있는 cudaStreamSynchronize은 필요하지 않아야한다는 점에 유의하십시오.

  1. CUDA 연산 (API 호출, 커널 호출, 다)에 관계없이 항상 Async API 또는 기타 고려 사항의 사용의 순차적으로을 실행합니다 같은 스트림에 발행 : 스트림 2 개의 실행 특성을 가지고있다.
  2. 별도의 스트림에 발행 된 cuda 조작은 모든 필수 요구 사항이 충족되었다고 가정하면 서로 비동기 적으로 실행됩니다. 마지막 경우 항목 1으로 인해

, 최종 그 스트림에 발행 된 이전 커널 호출도 cudaStreamSynchronize 호출하지 않고 완료 될 때까지 작업을 을 시작하지 않을 것이다 "데이터 위해 GPU가> 호스트 복사". 따라서 전화를 끊을 수 있다고 생각합니다. 예를 들어 2 번째 사례는 마지막 사례와 다르지 않아야하며 2 번째 사례에서는 수면 작업이 필요하지 않습니다. 동일한 스트림으로 발행 된 cudaMemcpy ...는 해당 스트림의 모든 이전 cuda 활동이 완료 될 때까지 시작되지 않습니다. 이것은 개울의 특성입니다.

EDIT2 : 여기에서 어떤 진전이 있는지 확신 할 수 없습니다. GTC preso here (슬라이드 21)에서 지적한 문제는 유효한 문제이지만 추가 동기화 작업을 삽입하여 해결할 수 없으며 "차단 커널"이 도움이되지도 않으며 기능도 아닙니다 하나의 카피 엔진을 가지고 있거나 2. 다른 스트림에서 커널을 발행하고 다른 개입 된 cuda 조작없이 순서대로 발급하려면 해당 위험이 존재합니다. 다음 슬라이드에서 지적했듯이이 문제를 해결하는 방법은 커널을 순차적으로 실행하지 않는 것입니다. 커널은 순차적으로 두 번째 경우와 유사합니다. 다시이 상태 것이다 :

가 당신은 당신의 사건이 좋은 동시성을주는 것을 확인했다
    • 는 그 경우 슬립 작업이 데이터 무결성

    필요하지 않습니다

  • 당신이 제공 할 경우 문제를 나타내는 간단한 샘플 코드, 아마도 다른 발견이 이루어질 수 있습니다.

  • +0

    도움 주셔서 감사합니다. 나는 당신의 모든 발언을 고려했다고 생각합니다. 커널에 리소스 문제가 없습니다. 모든 것은 GPU-> 호스트 memcopy 호출에 달려 있습니다. 나는 약간의 추가 조사를했고 위에 나의 결과를 발표했다. 아이디어가 있습니까? – user4811

    +0

    내 답변을 편집했습니다 –

    +0

    어둠 속에서 빛을 가져와 주셔서 감사합니다. 그러나 완료 될 때까지 어떻게 커널을 차단할 수 있습니까? 문제를 설명하는 예제를 내 질문에 추가했습니다. – user4811