장치 메모리에 할당된 CUDA 전역(C에서와 같은) 동적 배열

StackOverflow https://stackoverflow.com/questions/79121

  •  09-06-2019
  •  | 
  •  

문제

그래서 Nvidia의 CUDA 아키텍처를 활용하는 코드를 작성하려고 합니다.장치로 복사하거나 장치에서 복사하는 것이 전반적인 성능에 큰 타격을 준다는 것을 알았기 때문에 이제 많은 양의 데이터를 장치로 이동하려고 합니다.

이 데이터는 다양한 기능에 사용되기 때문에 글로벌하게 활용되었으면 좋겠습니다.예, 포인터를 전달할 수 있지만 이 경우 전역 작업 방법을 알고 싶습니다.

그래서 장치 할당 배열에 액세스하려는 장치 기능이 있습니다.

이상적으로는 다음과 같이 할 수 있습니다.

__device__ float* global_data;

main()
{
  cudaMalloc(global_data);
  kernel1<<<blah>>>(blah); //access global data
  kernel2<<<blah>>>(blah); //access global data again
}

그러나 동적 배열을 만드는 방법을 찾지 못했습니다.다음과 같이 배열을 선언하여 해결 방법을 찾았습니다.

__device__ float global_data[REALLY_LARGE_NUMBER];

cudaMalloc 호출이 필요하지 않지만 동적 할당 접근 방식을 선호합니다.

도움이 되었습니까?

해결책

아마도 이와 같은 것이 작동할 것입니다.

#include <algorithm>

#define NDEBUG
#define CUT_CHECK_ERROR(errorMessage) do {                                 \
        cudaThreadSynchronize();                                           \
         cudaError_t err = cudaGetLastError();                             \
         if( cudaSuccess != err) {                                         \
                     fprintf(stderr, "Cuda error: %s in file '%s' in line %i : %s.\n",    \
                                             errorMessage, __FILE__, __LINE__, cudaGetErrorString( err) );\
                     exit(EXIT_FAILURE);                                                  \
                 } } while (0)


__device__ float *devPtr;

__global__
void kernel1(float *some_neat_data)
{
    devPtr = some_neat_data;
}

__global__
void kernel2(void)
{
    devPtr[threadIdx.x] *= .3f;
}


int main(int argc, char *argv[])
{
    float* otherDevPtr;
    cudaMalloc((void**)&otherDevPtr, 256 * sizeof(*otherDevPtr));
    cudaMemset(otherDevPtr, 0, 256 * sizeof(*otherDevPtr));

    kernel1<<<1,128>>>(otherDevPtr);
    CUT_CHECK_ERROR("kernel1");

    kernel2<<<1,128>>>();

    CUT_CHECK_ERROR("kernel2");

    return 0;
}

한번 흔들어 보세요.

다른 팁

NVIDIA가 제공하는 풍부한 문서에 집중하는 데 시간을 투자하세요.

프로그래밍 가이드에서:

float* devPtr;
cudaMalloc((void**)&devPtr, 256 * sizeof(*devPtr));
cudaMemset(devPtr, 0, 256 * sizeof(*devPtr));

이는 메모리 할당 방법에 대한 간단한 예입니다.이제 커널에서 다음과 같이 float에 대한 포인터를 허용해야 합니다.

__global__
void kernel1(float *some_neat_data)
{
    some_neat_data[threadIdx.x]++;
}

__global__
void kernel2(float *potentially_that_same_neat_data)
{
    potentially_that_same_neat_data[threadIdx.x] *= 0.3f;
}

이제 다음과 같이 호출할 수 있습니다.

float* devPtr;
cudaMalloc((void**)&devPtr, 256 * sizeof(*devPtr));
cudaMemset(devPtr, 0, 256 * sizeof(*devPtr));

kernel1<<<1,128>>>(devPtr);
kernel2<<<1,128>>>(devPtr);

이 데이터는 수많은 기능으로 사용되므로 글로벌이되기를 바랍니다.

전역 변수를 사용하는 데에는 몇 가지 좋은 이유가 있습니다.이것은 확실히 하나가 아닙니다."devPtr"을 전역 범위로 이동하는 것을 포함하도록 이 예제를 확장하는 연습으로 남겨두겠습니다.

편집하다:

좋아, 근본적인 문제는 이것이다.커널은 장치 메모리에만 액세스할 수 있으며 사용할 수 있는 유일한 전역 범위 포인터는 GPU 포인터입니다.CPU에서 커널을 호출할 때 뒤에서 일어나는 일은 커널이 실행되기 전에 포인터와 기본 요소가 GPU 레지스터 및/또는 공유 메모리에 복사되는 것입니다.

그래서 제가 제안할 수 있는 가장 가까운 것은 다음과 같습니다.목표를 달성하려면 cudaMemcpyToSymbol()을 사용하세요.그러나 배경에서는 다른 접근 방식이 옳은 것일 수도 있다는 점을 고려하십시오.

#include <algorithm>

__constant__ float devPtr[1024];

__global__
void kernel1(float *some_neat_data)
{
    some_neat_data[threadIdx.x] = devPtr[0] * devPtr[1];
}

__global__
void kernel2(float *potentially_that_same_neat_data)
{
    potentially_that_same_neat_data[threadIdx.x] *= devPtr[2];
}


int main(int argc, char *argv[])
{
    float some_data[256];
    for (int i = 0; i < sizeof(some_data) / sizeof(some_data[0]); i++)
    {
        some_data[i] = i * 2;
    }
    cudaMemcpyToSymbol(devPtr, some_data, std::min(sizeof(some_data), sizeof(devPtr) ));
    float* otherDevPtr;
    cudaMalloc((void**)&otherDevPtr, 256 * sizeof(*otherDevPtr));
    cudaMemset(otherDevPtr, 0, 256 * sizeof(*otherDevPtr));

    kernel1<<<1,128>>>(otherDevPtr);
    kernel2<<<1,128>>>(otherDevPtr);

    return 0;
}

이 예에서는 '--host-compilation=c++'를 잊지 마세요.

나는 임시 포인터를 할당하고 이를 kernel1과 유사한 간단한 전역 함수에 전달하는 솔루션을 시도했습니다.

좋은 소식은 그것이 효과가 있다는 것입니다 :)

그러나 이제 "권고:전역 데이터에 액세스하려고 할 때마다 전역 메모리 공간을 가정하면 어떤 포인터가 가리키는지 알 수 없습니다."운 좋게도 가정은 정확했지만 경고는 짜증스럽습니다.

어쨌든, 기록을 위해 - 나는 많은 예제를 살펴봤고 "올바른!"이라는 출력을 얻는 것이 요점인 NVIDIA 연습을 실행했습니다.그런데, 난 쳐다보지도 않았어 모두 그들의.동적 전역 장치 메모리 할당을 수행하는 SDK 예제를 아는 사람이 있다면 여전히 알고 싶습니다.

음, 내 문제는 바로 devPtr을 전역 범위로 이동하는 문제였습니다.

전달된 데이터에 대한 포인터가 있는 두 개의 커널을 사용하여 정확하게 이를 수행하는 구현이 있습니다.나는 명시적으로 해당 포인터를 전달하고 싶지 않습니다.

나는 문서를 상당히 자세히 읽었고 nvidia 포럼을 방문했지만(Google은 한 시간 정도 검색했습니다) 실제로 실행되는 전역 동적 장치 배열의 구현을 찾지 못했습니다. 새롭고 흥미로운 방식으로 실패합니다).

SDK에 포함된 샘플을 확인해 보세요.이러한 샘플 프로젝트 중 다수는 예제를 통해 학습할 수 있는 적절한 방법입니다.

이 데이터는 다양한 기능에 사용되기 때문에 글로벌하게 활용되었으면 좋겠습니다.

-

전역 변수를 사용하는 데에는 몇 가지 좋은 이유가 있습니다.이것은 확실히 하나가 아닙니다."devptr"를 전역 범위로 옮기기 위해이 예를 확장하기위한 운동으로 남겨 두겠습니다.

커널이 배열로 구성된 대규모 const 구조에서 작동한다면 어떻게 될까요?소위 상수 메모리를 사용하는 것은 크기가 매우 제한되어 있기 때문에 옵션이 아닙니다.그럼 글로벌 메모리에 넣어야 하는데..?

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top