2014-04-08 6 views
4

fork()를 호출 한 후 "초기화 오류"가 발생합니다. 포크없이 같은 프로그램을 실행하면 모두 정상적으로 작동합니다.포크 후 CUDA 초기화 오류

if (fork() == 0) { 
    ... 
    cudaMalloc(....); 
    ... 
} 

어떻게 될까요?

완전한 예가 아래에 있습니다. cudaGetDeviceCount 호출을 주석으로 처리하면 정상적으로 작동합니다.

#include <stdio.h> 
#include <unistd.h> 
#include <stdlib.h> 
#include <sys/types.h> 
#include <sys/wait.h> 
#include <cuda_runtime.h> 

#define PERR(call) \ 
    if (call) {\ 
    fprintf(stderr, "%s:%d Error [%s] on "#call"\n", __FILE__, __LINE__,\ 
     cudaGetErrorString(cudaGetLastError()));\ 
    exit(1);\ 
    } 

int 
main(int argc, char **argv) 
{ 
    float *v_d; 
    int gpucount; 

    cudaGetDeviceCount(&gpucount); 

    if (fork() == 0) { 
    cudaSetDevice(0); 
    PERR(cudaMalloc(&v_d, 1000*sizeof(float))); 
    } 
    wait(NULL); 
    return 0; 
} 

간단한 메이크 :이 경우

PROGS = fork 
CUDA_PATH = /usr/local/cuda 
CXXFLAGS = -g -O0 -Wall 
CXXINCLUDES = -I$(CUDA_PATH)/include 
NVCC := $(CUDA_PATH)/bin/nvcc -ccbin $(CXX) -Xcompiler "$(CXXFLAGS)" 

fork: fork.cxx 
     $(NVCC) $^ -o [email protected] $(LIBS) 

clean: 
     (rm $(PROGS) *.o) 

, 나는 단지 부모 프로세스 내에서 사용할 수있는 장치의 수를 얻기 위해 노력하고 있어요. 이 해결 방법은 작업을 수행합니다

if (fork() == 0) { 
    PERR(cudaGetDeviceCount(&gpucount)); 
    return(gpucount); 
    } 
    wait(&gpucount); 
    gpucount = WEXITSTATUS(gpucount); 
+0

당신이 보여주는 전체 코드를 제공 할 수 무엇 당신은 일을? '포크'와'쿠다 맥록 '에 문제가 없었습니다. –

+0

나는 단서가 있다고 생각합니다. 프로그램은 포크 앞에 cudaSetDevice를 호출하고있었습니다. 포크에서 호출을 이동하면 실행됩니다. 나는 작은 모범을 보일 것이다. – Bob

답변

4

fork() 아이 과정을 만듭니다. 프로세스는 자체 주소 공간을 가지고 있습니다. CUDA 컨텍스트는 두 가지 다른 프로세스간에 공유 될 수 없습니다. 그 이유 중 하나는 다양한 주소 공간에서 다양한 포인터가 의미가 없다는 것입니다.

fork() 이전에 CUDA 컨텍스트를 만들면 하위 프로세스 내에서 사용할 수 없습니다. 쿠다 컨텍스트를 공유하는 cudaSetDevice(0); 호출 시도는 암시가 암시 한대로 하나가 부모 프로세스 또는 자식 프로세스에서 CUDA 작업을 수행하는 것입니다, cudaGetDeviceCount();

솔루션 호출 부모 프로세스에서 생성 . 멀티 디바이스 시스템을 사용하는 경우 별도의 디바이스를 별도의 프로세스에 할당 할 수 있어야합니다 (CUDA simpleIPC sample code은 정확히 이것을 수행합니다). (요점은 포크 앞에 CUDA 컨텍스트를 만들지 않는 것입니다.)

this question/answerthis one에 관심이있을 수 있습니다.

가 여기에 완벽하게 일을 예입니다 별도의 GPU를 사용하여 자식 프로세스와 부모 프로세스를 보여주는 (2 개 CUDA 장치 필요) :

$ cat t345.cu 
#include <unistd.h>  /* Symbolic Constants */ 
#include <sys/types.h> /* Primitive System Data Types */ 
#include <errno.h>  /* Errors */ 
#include <stdio.h>  /* Input/Output */ 
#include <sys/wait.h> /* Wait for Process Termination */ 
#include <stdlib.h>  /* General Utilities */ 


#define cudaCheckErrors(msg) \ 
    do { \ 
     cudaError_t __err = cudaGetLastError(); \ 
     if (__err != cudaSuccess) { \ 
      fprintf(stderr, "Fatal error: %s (%s at %s:%d)\n", \ 
       msg, cudaGetErrorString(__err), \ 
       __FILE__, __LINE__); \ 
      fprintf(stderr, "*** FAILED - ABORTING\n"); \ 
      exit(1); \ 
     } \ 
    } while (0) 


__global__ void addkernel(int *data){ 
    *data += 1; 
} 

int main() 
{ 
    pid_t childpid; /* variable to store the child's pid */ 
    int retval;  /* child process: user-provided return code */ 
    int status;  /* parent process: child's exit status */ 

    /* only 1 int variable is needed because each process would have its 
     own instance of the variable 
     here, 2 int variables are used for clarity */ 

    /* now create new process */ 
    childpid = fork(); 

    if (childpid >= 0) /* fork succeeded */ 
    { 
     if (childpid == 0) /* fork() returns 0 to the child process */ 
     { 
      printf("CHILD: I am the child process!\n"); 
      printf("CHILD: Here's my PID: %d\n", getpid()); 
      printf("CHILD: My parent's PID is: %d\n", getppid()); 
      printf("CHILD: The value of my copy of childpid is: %d\n", childpid); 
      int *h_a, *d_a; 
      h_a = (int *)malloc(sizeof(int)); 
      cudaSetDevice(0); 
      cudaCheckErrors("CHILD cudaSetDevice fail"); 
      cudaMalloc(&d_a, sizeof(int)); 
      cudaCheckErrors("cudaMalloc fail"); 
      *h_a = 1; 
      cudaMemcpy(d_a, h_a, sizeof(int), cudaMemcpyHostToDevice); 
      cudaCheckErrors("cudaMemcpy H2D fail"); 
      addkernel<<<1,1>>>(d_a); 
      cudaDeviceSynchronize(); 
      cudaCheckErrors("kernel fail"); 
      cudaMemcpy(h_a, d_a, sizeof(int), cudaMemcpyDeviceToHost); 
      cudaCheckErrors("cudaMemcpy D2H fail"); 
      printf("CHILD: result: %d\n", *h_a); 

      printf("CHILD: Sleeping for 1 second...\n"); 
      sleep(1); /* sleep for 1 second */ 
      cudaDeviceReset(); 
      printf("CHILD: Enter an exit value (0 to 255): "); 
      scanf(" %d", &retval); 
      printf("CHILD: Goodbye!\n"); 
      exit(retval); /* child exits with user-provided return code */ 
     } 
     else /* fork() returns new pid to the parent process */ 
     { 
      printf("PARENT: I am the parent process!\n"); 
      printf("PARENT: Here's my PID: %d\n", getpid()); 
      printf("PARENT: The value of my copy of childpid is %d\n", childpid); 
      printf("PARENT: I will now wait for my child to exit.\n"); 
      int *h_a, *d_a; 
      h_a = (int *)malloc(sizeof(int)); 
      cudaSetDevice(1); 
      cudaCheckErrors("PARENT cudaSetDevice fail"); 
      cudaMalloc(&d_a, sizeof(int)); 
      cudaCheckErrors("cudaMalloc fail"); 
      *h_a = 2; 
      cudaMemcpy(d_a, h_a, sizeof(int), cudaMemcpyHostToDevice); 
      cudaCheckErrors("cudaMemcpy H2D fail"); 
      addkernel<<<1,1>>>(d_a); 
      cudaDeviceSynchronize(); 
      cudaCheckErrors("kernel fail"); 
      cudaMemcpy(h_a, d_a, sizeof(int), cudaMemcpyDeviceToHost); 
      cudaCheckErrors("cudaMemcpy D2H fail"); 
      printf("PARENT: result: %d\n", *h_a); 
      wait(&status); /* wait for child to exit, and store its status */ 
      printf("PARENT: Child's exit code is: %d\n", WEXITSTATUS(status)); 
      cudaSetDevice(0); 
      cudaCheckErrors("PARENT cudaSetDevice 2 fail"); 
      int *h_a2, *d_a2; 
      cudaMalloc(&d_a2, sizeof(int)); 
      cudaCheckErrors("cudaMalloc fail"); 
      h_a2 = (int *)malloc(sizeof(int)); 
      *h_a2 = 5; 
      cudaMemcpy(d_a2, h_a2, sizeof(int), cudaMemcpyHostToDevice); 
      cudaCheckErrors("cudaMemcpy H2D fail"); 
      addkernel<<<1,1>>>(d_a2); 
      cudaDeviceSynchronize(); 
      cudaCheckErrors("kernel fail"); 
      cudaMemcpy(h_a2, d_a2, sizeof(int), cudaMemcpyDeviceToHost); 
      cudaCheckErrors("cudaMemcpy D2H fail"); 
      printf("PARENT: result2: %d\n", *h_a2); 
      printf("PARENT: Goodbye!\n"); 
      exit(0); /* parent exits */ 
     } 
    } 
    else /* fork returns -1 on failure */ 
    { 
     perror("fork"); /* display error message */ 
     exit(0); 
    } 
} 
$ nvcc -arch=sm_20 -o t345 t345.cu 
$ ./t345 
CHILD: I am the child process! 
CHILD: Here's my PID: 23603 
CHILD: My parent's PID is: 23602 
CHILD: The value of my copy of childpid is: 0 
PARENT: I am the parent process! 
PARENT: Here's my PID: 23602 
PARENT: The value of my copy of childpid is 23603 
PARENT: I will now wait for my child to exit. 
CHILD: result: 2 
CHILD: Sleeping for 1 second... 
PARENT: result: 3 
CHILD: Enter an exit value (0 to 255): 10 
CHILD: Goodbye! 
PARENT: Child's exit code is: 10 
PARENT: result2: 6 
PARENT: Goodbye! 
$ 

(here에서 수정)

+0

"Toolkit Documentation"에 언급되지 않은 것에 실망합니다. "0_Simple/simpleIPC/simpleIPC.cu"예제에서는이 문제를 해결하는 방법을 설명합니다. 또한 사용 사례에 대한 간단한 해결 방법을 원래 질문에 추가했습니다. "cudaDeviceReset();"에서 잘못된 오해의 소지가있는 호출이 있습니다. 근본적인 맥락을 재설정하기 위해 아무 것도하지 않는 것 같습니다. – Bob

+0

하지만 포크 전에'cudaGetDeviceCount'를 호출해야한다면? 여러 GPU를 사용하고 GPU 당 하나의 프로세스를 사용하는 경우 미리 생성 할 프로세스의 수를 알고 싶습니다. – landau

+0

GPU 당 하나의 프로세스보다 GPU 당 하나의 스레드를 사용하는 것이 좋습니다. 프로세스를 사용하려면 프로그램에서 처리 할 수있는 최대 GPU 수에 대한 프로세스를 만듭니다 (이 프로세스는 아마 8보다 크지 않습니다). 각 프로세스는 고유 한 ID를 가지며'cudaGetDeviceCount'를 질의 할 수 있습니다. 해당 프로세스에 해당하는 GPU가없는 경우 프로세스가 종료됩니다. 예를 들어 4 개의 GPU를 가지고 있지만 8 개의 프로세스를 돌리면 0-3의 프로세스는 각각 GPU를 얻고 4-7 개의 프로세스는 GPU가 4 개뿐이므로 방금 종료됩니다.나는 다른 방법도 있다고 확신한다. –