수색…


소개

OpenCL을 사용하기 전에 코드를 사용하여 OpenCL을 사용하도록 설정해야합니다. 이 주제는 프로젝트에서 opencl을 실행하여 기본 커널을 실행하는 방법에 중점을 둡니다. 예제는 C # 래퍼 OpenCL.NET을 기반으로하지만 래퍼는 OpenCL에 추상화를 추가하지 않으므로 코드는 C / C ++에서도 거의 변경되지 않고 실행됩니다.

C #의 호출은 다음과 같습니다. 'Cl.GetPlatformIDs'. C 스타일의 OpenCL API의 경우 'clGetPlatformIDs'를 호출하고 C ++ 스타일의 경우 'cl :: GetPlatformIDs'

비고

  • NVidia, AMD 및 Intel은 OpenCL과 약간 다른 구현 방식을 가지고 있지만 차이점은 괄호 요구 사항과 암시 적 캐스트에만 국한됩니다. 때로는 NVidia가 메소드의 올바른 오버로드를 파악하는 동안 커널을 손상시킵니다. 이 경우 GPU를 지원하기 위해 명시 적 캐스트를 제공하는 것이 좋습니다. 문제점은 런타임 컴파일 된 커널에서 관찰되었습니다.

  • 이 주제에서 사용 된 호출에 대한 자세한 정보를 얻으려면 함수 이름 앞에 google 'OpenCL'이면 충분합니다. Khronos 그룹에는 웹 사이트에서 사용할 수있는 모든 매개 변수와 데이터 유형에 대한 완벽한 문서가 있습니다.

대상 장치 초기화

OpenCL 커널은 GPU 또는 CPU에서 실행될 수 있습니다. 이렇게하면 고객이 시스템이 오래되었을 수있는 대체 솔루션을 사용할 수 있습니다. 프로그래머는 기능을 CPU 또는 GPU로 제한 할 수도 있습니다.

OpenCL을 사용하려면 '컨텍스트'와 '장치'가 필요합니다. 둘 다 OpenCL API (cl :: Context 또는 clContext & ~ Device라고도 함)로 정의 된 구조이며 사용 된 대상 프로세서를 정의합니다.

장치와 컨텍스트를 얻으려면 각각이 여러 장치를 호스트 할 수있는 사용 가능한 플랫폼 목록을 쿼리해야합니다. 플랫폼은 실제 GPU와 CPU를 나타내며 장치는 포함 된 컴퓨팅 장치를 더 구분할 수 있습니다. GPU의 경우 대부분의 플랫폼에는 하나의 장치 만 있습니다. 그러나 CPU는 CPU 기능 옆에 추가로 통합 된 GPU를 제공 할 수 있습니다.

컨텍스트는 메모리, 명령 대기열, 다른 커널 및 프로그램을 관리합니다. 컨텍스트는 단일 장치로 제한 될 수 있지만 여러 장치를 참조 할 수도 있습니다.

코딩을 시작하기 전에 간단한 API 노트 : OpenCL에 대한 거의 모든 호출은 반환 값 또는 참조 값 (C의 포인터)을 통해 오류 값을 제공합니다. 이제 시작할 수 있습니다.

ErrorCode err;
var platforms = Cl.GetPlatformIDs(out err);
if(!CheckError(err, "Cl.GetPlatformIDs")) return;
foreach (var platform in platforms) {
    foreach (var device in Cl.GetDeviceIDs(platform, DeviceType.Gpu, out err)) {
        if(!CheckError(err, "Cl.GetDeviceIDs")) continue;
        [...]
    }
}

이 코드 스 니펫은 시스템에서 사용 가능한 모든 GPU 장치를 쿼리합니다. 이제 목록에 추가하거나 첫 번째 일치 항목을 사용하여 컨텍스트를 직접 시작할 수 있습니다. 'CheckError (...)'함수는 오류 코드에 성공 여부 또는 다른 값이 있는지 확인하고 일부 로깅을 제공 할 수있는 간단한 유틸리티입니다. 별도의 함수 또는 매크로를 사용하는 것이 좋습니다. 매크로를 많이 호출하기 때문입니다.

ErrorCode는 C #의 cl_int 데이터 유형에 대한 열거 형이지만 C / C ++에서는 int 값과 미리 정의 된 오류 상수를 비교할 수 있습니다 ( https://www.khronos.org/registry/OpenCL/sdk/1.0/docs/man/). xhtml / errors.html

또한 장치가 필요한 모든 기능을 지원하는지 여부를 확인할 수도 있습니다. 그렇지 않으면 런타임에 커널이 중단 될 수 있습니다. 다음을 사용하여 장치 기능을 쿼리 할 수 ​​있습니다.

Cl.GetDeviceInfo(_device, DeviceInfo.ImageSupport, out err)

이 예제는 장치에 이미지 기능을 실행할 수 있는지 여부를 묻습니다. 다음 단계와 마지막 단계를 위해 수집 된 장치에서 컨텍스트를 구성해야합니다.

_context = Cl.CreateContext(null, 1, new[] { _device }, ContextNotify, IntPtr.Zero, out err);

여기에 어떤 일이 일어나고 있습니다. C / C ++ 사용자의 경우 IntPtr은 C #의 포인터 주소입니다. 여기서 중요한 부분에 집중하겠습니다.

  • 두 번째 매개 변수는 사용할 장치의 수를 정의합니다
  • 세 번째 매개 변수는 해당 장치의 배열 (또는 C / C ++의 포인터)입니다.
  • 세 번째 매개 변수는 콜백 함수의 함수 포인터입니다. 이 함수는 컨텍스트 내에서 오류가 발생할 때마다 사용됩니다.

더 많이 사용하려면 중고 장치와 컨텍스트를 어딘가에 보존해야합니다.

모든 OpenCL 상호 작용이 끝나면 다시 컨텍스트를 릴리스해야합니다.

Cl.ReleaseContext(_context);

커널 컴파일하기

커널은 런타임에 대상 장치에서 컴파일 할 수 있습니다. 그렇게하려면

  • 커널 소스 코드
  • 컴파일 할 대상 장치
  • 타겟 디바이스로 구축 된 컨텍스트

빠른 용어 업데이트 : 프로그램에 커널 모음이 포함되어 있습니다. 커넬은 그 파일의 다른 기능 멤버 인 반면, 프로그램은 완전한 C / C ++ / C # 소스 파일이라고 생각할 수 있습니다.

먼저 소스 코드에서 프로그램을 만들어야합니다.

var program = Cl.CreateProgramWithSource(_context, 1, new[] { source }, null, out err);

여러 소스 파일을 하나의 프로그램에 결합하여 함께 컴파일하면 서로 다른 파일에 커널을 가지고 한 번에 컴파일 할 수 있습니다.

다음 단계에서는 대상 장치에서 프로그램을 컴파일해야합니다.

err = Cl.BuildProgram(program, 1, new[] { _device }, string.Empty, null, IntPtr.Zero);

이제 약간의주의 사항이 있습니다. 오류 코드는 함수 호출 자체가 성공했는지 여부를 알려주지 만 코드가 실제로 컴파일되었는지 여부는 알려주지 않습니다. 이를 확인하기 위해 몇 가지 추가 정보를 쿼리해야합니다.

BuildStatus status;
status = Cl.GetProgramBuildInfo(program, _device, ProgramBuildInfo.Status, out err).CastTo<BuildStatus>();
if (status != BuildStatus.Success) {
    var log = Cl.GetProgramBuildInfo(program, _device, ProgramBuildInfo.Log, out err);
}

C / C ++ 사람들은 끝에있는 캐스트를 무시하고 반환 된 정수를 상수와 비교할 수 있습니다.

첫 번째 호출은 빌드가 실제로 성공했는지 여부를 확인합니다. 그렇지 않다면 로그를 조사하여 문제가 발생한 곳을 정확히 볼 수 있습니다. 다른 플랫폼에 관한 몇 가지 공통점은 참고하십시오.

일단 프로그램이 빌드되면 컴파일 된 프로그램에서 다른 커널을 추출해야합니다. 이렇게하려면 커널을 다음과 같이 만드십시오.

_kernel = Cl.CreateKernel(_program, kernel, out err);

여기서 'kernel'은 커널 이름의 문자열입니다. 커널을 다 마쳤 으면 다음과 같이 배포해야합니다.

Cl.ReleaseKernel(_kernel);

명령 대기열 만들기

장치에서 작업을 시작하려면 각 장치에 대한 명령 대기열이 필요합니다. 대기열은 대상 장치에 대해 수행 한 다른 호출을 추적하여 순서대로 유지합니다. 대부분의 명령은 블로킹 또는 비 블로킹 모드에서 실행될 수도 있습니다.

대기열을 만드는 것은 매우 간단합니다.

_queue = Cl.CreateCommandQueue(_context, _device, CommandQueueProperties.None, out err);

명령 대기열과의 기본적인 상호 작용은 수행하려는 다른 작업을 대기열에 넣는 것입니다 (예 : 장치와주고받는 데이터 복사 및 커널 실행).

명령 대기열 사용을 마쳤 으면에 대한 호출로 대기열을 해제해야합니다.

Cl.ReleaseCommandQueue(_queue);

커널 실행하기

자 이제 우리는 병렬 장치에서 커널을 실행하면서 실제 물건으로갑니다. 커널 디스 패칭을 완전히 이해하려면 하드웨어 기본 사항을 읽어보십시오.

먼저 커널을 실제로 호출하기 전에 커널 인수를 설정해야합니다. 이것은 통해 이루어집니다

err = Cl.SetKernelArg(_kernel, $argumentIndex, $argument);

커널을 시작하기 전에 모든 인수를 설정하지 않으면 커널이 실패합니다.

실제로 커널을 시작하기 전에 '전역 작업 크기'와 '로컬 작업 크기'를 계산해야합니다.

전역 작업 크기는 GPU에서 시작될 총 스레드 수입니다. 로컬 작업 크기는 각 스레드 블록 내부의 스레드 수입니다. 커널이 특별한 요구 사항을 필요로하지 않으면 로컬 작업 크기를 생략 할 수 있습니다. 그러나 현지 작업 규모가 주어지면 글로벌 작업 규모는 현지 작업 규모의 배수 여야합니다.

작업 크기는 1 차원, 2 차원 또는 3 차원 일 수 있습니다. 원하는 차원 수에 대한 선택은 전적으로 귀하에게 달려 있으며 귀하의 알고리즘에 가장 적합한 것을 고를 수 있습니다.

이제 작업 크기를 결정 했으므로 커널을 호출 할 수 있습니다.

Event clevent;
err = Cl.EnqueueNDRangeKernel(_queue, _kernel, $dimensions, null, $globalWorkSize, $localWorkSize, 0, null, out clevent);

$ dimension은 우리가 원하는 차원의 수를 정의합니다. $ globalWorkSize는 크기가 $ dimension이고 전역 Work size가 있고 $ localWorkSize도 마찬가지입니다. 마지막 인자는 현재 실행중인 커널을 나타내는 객체를 제공합니다.



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow