(4) CUDA 응용 프로그래밍 인터페이스에 대한 자세한 설명

C 언어 확장

CUDA의 프로그래밍 인터페이스는 C 언어의 확장 세트이며, 주요 인터페이스는 호스트 구성 요소, 장치 구성 요소 및 공용 구성 요소의 세 가지 구성 요소로 구분되는 런타임 라이브러리입니다.

호스트 구성 요소: 호스트에서 실행되며 하나 이상의 컴퓨팅 장치를 제어하고 액세스하는 기능을 제공합니다.

장치 구성 요소: 장치가 실행되고 장치별 기능을 제공합니다.

공통 구성 요소: 내장 벡터 유형을 제공하고 호스트 및 장치 인코딩을 모두 지원하는 C 표준 라이브러리의 하위 집합입니다.


질문하다

  1. 기능 정의 및 선언형 한정문, 호스트와 장치(GPU)의 기능은 어떻게 호출되나요?
  2. GPU의 메모리 위치와 크기를 어떻게 정의하나요?
  3. 커널의 기능이 장치에서 어떻게 실행되나요?
  4. GPU 블록 및 스레드 표시기를 호출하는 방법은 무엇입니까?
  5. CUDA 코드를 컴파일하는 방법은 무엇입니까?

기능 유형 자격 설명

__device__ // 在设备上执行,仅可从设备上调用

__global__ // 限定一个函数作为kernel的存在,在设备上执行,仅可从主机上调用

__host__ // 在主机上执行,仅可从主机上调用

/*
__host__ 可以与 __device__ 限定词的组合,支持主机和设备双向编译
*/

/*
限定:
__device__ 和 __global__ 不支持递归、内部不能声明静态变量、不能有自变量的一个变量数字
__device__ 不能拿到函数地址,另外,函数指向 __global__ 是支持的
__global__ 和 __host__ 不能一起使用
__global__ 函数必须要void的返回类型,调用到__global__函数必须指定它的执行配置
__global__ 函数的调用是同步的,意味着设备执行完成前返回
__global__函数参数目前是通过共享内存到设备的,并且被限制在256 个字节
*/

변수 유형 자격 선언문

__device__
__device__한정자는 장치에 있는 변수를 선언합니다.
최대 하나의 다른 유형 한정자가 다음 세 항목에 정의되어 있으며, 변수가 속한 메모리 공간을 추가로 지정하기 위해 함께 사용할
수 있습니다 . 이 변수는 다음과 같습니다.__device__

  1. 전역 메모리 공간에 상주
  2. 응용 프로그램 수명이 있습니다
  3. 그리드 내의 모든 스레드와 런타임 라이브러리를 통해 호스트에서 액세스 가능

__constant__
__constant__무작위로 사용되는 한정자는 __device__변수를 선언합니다.

  1. 일정한 기억 공간에 상주하다
  2. 응용 프로그램 수명이 있습니다
  3. 그리드 내의 모든 스레드와 런타임 라이브러리를 통해 호스트에서 액세스 가능

__shared__
__shared__select와 함께 사용되는 한정자는 __device__변수를 선언합니다.

  1. 스레드 블록의 공유 메모리 공간에 상주합니다.
  2. 블록의 수명이 있습니다
  3. 블록 내의 모든 스레드에만 접근 가능

스레드 간에 공유되는 변수는 완전한 순차 일관성을 가집니다.
실행 중인 __syncthreads()함수만 다른 스레드의 쓰기에서 볼 수 있도록 보장됩니다
. 변수가 휘발성으로 정의되지 않는 한, 컴파일러는 이전 상태에 도달하자마자 공유 메모리를 자유롭게 최적화할 수 있습니다. ~에

예를 들어 공유 메모리의 변수를 외부 배열로 선언하는 경우 extern_shared_float shared[];
배열의 크기는 전송 시 결정됩니다. 이런 방식으로 선언된 모든 변수는 메모리의 동일한 주소에서 시작하므로
배열의 변수 배치는 offset(오프셋)에 의해 명시적으로 제어
되어야 합니다.

short array0[128];
float array1[64];
int array2[256];

동적으로 할당된 공유 메모리에서는 다음과 같은 방법으로 배열을 정의할 수 있습니다.

extern __shared__ short array[];
__device__ void func() //__device__or__global__function
{
    
    
 Short* array0 = (short*)array;
 float* array1 = (float*)&array0[128];
 int* array2 = (int*)&array1[64];
} 

한정

이러한 한정자는 함수의 : structunion멤버, 형식 매개변수 및 지역 변수가 호스트에서 실행되는 것을 허용하지 않습니다.

__shared__변수 __constant__는 정적 저장소를 의미합니다.

__device__,__shared__변수 는 외부 사용을 위한 키워드로 정의 __constant__할 수 없습니다.extern

__device__변수 __constant__는 파일 범위에서만 허용됩니다.

__constant__변수는 장치에서 할당할 수 없으며, 호스트 런타임 기능을 통해 호스트에서만 할당할 수 있습니다.

__shared__변수는 선언의 일부로 초기화될 수 없습니다.

한정자 없이 장치 코드에 선언된 인수는 일반적으로 레지스터에 있습니다.

어떤 경우에는 컴파일러가 이를 로컬 메모리에 배치하도록 선택할 수도 있습니다.

일반적으로 이 경우 큰 구조나 배열은 많은 레지스터 공간을 소비하며 컴파일러는 배열이 상수로 인덱스되어 있는지 확인할 수 없습니다.

ptx- ptx또는 옵션을 사용 하여 컴파일하여 얻은 어셈블리 코드 검사는 -keep변수가 첫 번째 컴파일 단계에서 로컬 메모리에 배치되었는지 여부를 나타내므로 니모닉을 사용하여 선언되거나 .localld.local니모닉 을 사용하여 st.local액세스 됩니다.

그렇지 않은 경우 대상 아키텍처에 대해 너무 많은 레지스터 공간을 소비하더라도 후속 컴파일 단계에서 다른 결정을 내릴 수 있습니다.

--ptxas-option=-v를 사용하여 로컬 메모리 사용량 보고서(lmem)를 생성할 수 있습니다 .

컴파일러가 전역 메모리 공간을 가리키는지 로컬 메모리 공간을 가리키는지 확인할 수 있는 경우 장치에서 실행되는 포인터 코드 지원

그렇지 않으면 주소가 지정된 메모리나 전역 메모리에 선언된 공간만 가리키도록 제한됩니다.

호스트에서 실행되는 전역 또는 공유 메모리의 코드 또는 장치에서 실행되는 호스트 메모리 코드에 대한 포인터를 해제합니다.

그 결과 정의되지 않은 동작이 발생하여 조각 오류가 발생하거나 애플리케이션이 종료됩니다.

주소는 장치 코드의 __device__,__shared__또는 변수를 통해서만 얻을 수 있습니다.__constant__

여기서 __device__또는 __constant__변수의 주소는 다음을 통해서만 cudaGetSymbolAddress()얻을 수 있습니다.


구성 실행

모든 __global__함수 호출은 실행 구성을 지정해야 합니다.

실행 구성은 일반적으로 장치에서 실행되는 기능의 그리드 및 블록 크기를 정의합니다 stream.

함수 이름과 괄호로 묶인 인수 사이에 삽입하여 <<< Dg, Db, Ns,S>>>지정합니다.

Dg유형 이며 전송되는 블록 수와 동일하도록 dim3래스터의 차원과 크기를 지정합니다 .Dg.x * Dg.y

Db유형 dim3이며 각 블록의 크기와 크기를 지정합니다(예: Db.x * Db.y * Db.z블록당 스레드 수와 동일).

Nssize_t 유형이며 정적으로 할당된 메모리 외에 각 블록에 대해 동적으로 할당되는 공유 메모리의 바이트 수를 지정합니다. 이 동적으로 할당된 메모리는 외부 배열로 선언된 모든 변수에 의해 사용됩니다. Ns는 기본값인 선택적 매개변수입니다. 0으로.

ScudaStream_t 유형이고 관련 스트림을 지정하며 S기본값은 0인 선택적 숫자입니다.

예를 들어 함수는 다음과 같이 선언됩니다.

__global__ void Func(float* parameter);

다음과 같이 호출해야 합니다.

Func<<< Dg, Db, Ns >>>(parameter);

구성을 수행하는 기능 매개변수는 호출되기 전에 평가되어 공유 메모리를 통해 장치에 전달됩니다.

Dg 또는 Db가 장치에서 허용하는 최대값보다 크거나 Ns의 값이 마이너스(공유 메모리에 정적으로 할당된 메모리, 함수 매개변수 및 실행 구성)보다 큰 경우 함수가 호출되지 않습니다.


내장 변수

/*
gridDim
这个变量是类型dim3 (参见4.3.1.2 部分)并且包含栅格的维数。

blockIdx
这变量是类型uint3 (参见4.3.1.1 部分)并且包含栅格之内的块索引。

blockDim
这变量是类型dim3 (参见4.3.1.2 部分)并且包含在块的维数。

threadIdx
这变量是类型uint3 (参见4.3.1.1 部分)并且包含块之内的线程索引。

限定
 内置变量不允许取得任何地址。
 不允许赋值到任何内置变量。 

*/


NVCC 컴파일

nvccCUDA 코드 컴파일 프로세스를 위한 컴파일러 드라이버의 약어입니다. 간단하고 친숙한 명령줄 옵션을 제공하고 다양한 컴파일 단계 모음을 구현하는 도구를 호출하여 이를 실행합니다.

nvcc기본 작업 흐름은 장치 코드를 호스트 코드에서 분리하고 장치 코드를 바이너리 형식이나 cubin개체 로 컴파일하는 것입니다.

다른 도구를 사용하여 컴파일하기 위해 제출된 C 코드 또는 최종 컴파일 단계에서 호스트 컴파일러를 직접 호출하는 개체 코드로 생성된 호스트 코드 출력

애플리케이션은 생성된 호스트 코드를 무시하고 장치에 로드된 CUDA드라이버 개체를 사용하거나 생성된 호스트 코드를 연결할 수 있습니다.APIcubin

코드에는 전역적으로 초기화된 데이터 배열로 개체가 포함되어 cubin있으며 구성 구문을 구현하는 스위치가 포함되어 있으며 CUDA Runtime컴파일된 각 Kernel.

컴파일러가 CUDA소스 파일의 프런트 엔드에서 처리하는 C++구문은 완전히 준수됩니다.

호스트 코드가 완벽하게 지원됩니다 C++. 그러나 장치 코드는 다음 중 일부만 지원 C++합니다 C.

다음과 같은 기본 블록의 C++기능 classes, inheritance이나 변수 선언은 지원되지 않습니다.

C++구문을 사용하면 포인터를 사용하지 않고 포인터를 포인터 에 할당할 수 없습니다 void(예: malloc()를 반환하여) .typecastnon-void

다음은 NVCC의 두 가지 컴파일러 감지를 소개합니다.

__noinline__

기본적으로 __device__함수는 항상 인라인입니다.

__noinline__함수는 인라인이 아닌 함수에 대한 힌트로 사용될 수 있습니다.

함수 자체는 호출 파일에 배치되어야 하며 컴파일러는 __noinline__포인터 매개변수가 있는 함수와 큰 매개변수 목록이 있는 함수에 대한 한정자가 제대로 작동한다고 보장할 수 없습니다.

#pragma unroll

기본적으로 컴파일러는 알려진 이동 횟수에 대해 작은 루프를 펼칩니다.

#pragma unroll펼쳐진 루프를 감지하고 제어할 수 있습니다.

이 루프 앞에 배치되어야 하며 이 루프에만 영향을 미칩니다. 동시에 매개변수를 통해 루프를 확장할 수 있는 횟수를 지정할 수 있습니다.

예를 들어:

#pragma unroll 5
For (int i = 0; i < n; ++i)

루프가 5번 펼쳐집니다. 확장 작업이 프로그램의 정확성에 영향을 미치지 않는지 확인하십시오.
연결된 값이 없으면 #pragma unroll이동 횟수가 일정할 때 루프가 완전히 풀리고, 그렇지 않으면 풀리지 않습니다.


공개 런타임 구성요소

공통 런타임 구성요소는 호스트 및 장치 기능 모두에서 사용할 수 있습니다.

内置矢量类型
char1, uchar1, char2, uchar2, char3, uchar3, char4, uchar4, short1, ushort1,
short2, ushort2, short3, ushort3, short4, ushort4, int1, uint1, int2, uint2, int3,
uint3, int4, uint4, long1, ulong1, long2, ulong2, long3, ulong3, long4, ulong4,
float1, float2, float3, float4

이러한 벡터 유형은 기본 정수 및 부동 소수점 유형에서 파생됩니다.

이는 구조체이며 첫 번째, 두 번째, 세 번째 및 네 번째 구성 요소는 각각 x, y, z,및 필드를 통해 액세스할 수 있습니다.w

그들은 모두 형식에서 make_<type name>생성자 함수를 사용합니다.

예: int2 make_int2(int x, int y);할당을 통한 유형 의 벡터 (x, y)생성int2

dim3 类型 

이 유형은 uint3다음을 기준으로 차원을 지정하는 정수 벡터 유형 입니다.

유형의 변수를 정의할 때 dim3지정되지 않은 나머지 모든 구성요소는 다음으로 초기화됩니다.1

数学函数

여기에 이미지 설명을 삽입하세요
여기에 이미지 설명을 삽입하세요
여기에 이미지 설명을 삽입하세요
Table B-1수학 함수는 호스트 및 장치 함수로 사용될 수 있으며, 각 함수의 오류 한계는 집중적으로 테스트되었지만 절대적으로 정확하다고 보장되지는 않습니다.

덧셈과 곱셈은 IEEE를 준수하므로 최대 오류는 0.5ulp입니다. 이들은 일반적으로 하나의 FMAD(곱셈 덧셈 명령어)로 결합됩니다.

부동 소수점 수를 정수로 계산할 때 roundf() 대신 rintf()를 사용하는 것이 좋습니다.

roundf()는 8개의 명령어 시퀀스를 매핑하는 반면 rintf()는 하나의 명령어만 매핑하기 때문입니다.

truncf(), ceilf() 및 Floorf()도 하나의 명령어만 매핑합니다.

CUDA 런타임 라이브러리는 또한 명령어를 매핑하는 정수 min() 및 max()를 지원합니다.

여기에 이미지 설명을 삽입하세요

Table B-2기능은 장치 기능으로만 사용할 수 있습니다.

오류 범위는 GPU에 따라 다릅니다.

이러한 함수는 정밀도는 낮지만 표 B-1의 일부 함수보다 훨씬 빠릅니다. 접두사가 동일합니다.

__fadd_rz(x,y)0으로 반올림하여 부동 소수점 매개변수 x 및 y를 계산하고 __fmul_rz(x,y)0으로 반올림하여 부동 소수점 매개 변수 xx를 계산합니다.xyyy 제품

일반 부동 소수점 나누기는 __fdividef(x,y)정밀도가 동일하지만 2 126 < y < 2 128 2^{126} < y < 2^{128}2126<와이<2128

__fdividef(x,y)결과는 0이고 일반 나누기는 올바른 결과를 얻을 수 있습니다. 마찬가지로 2 126 < y < 2 128 2^{126}< y <2^{128}2126<와이<2xx인 경우 128x 는 무한대

__fdividef(x,y)결과는 다음과 같습니다 NaN(결과는 무한대에 0을 곱함). 정규 나누기는 무한대를 반환합니다.

__[u]mul24(x,y)정수 인수 x와 y의 최하위 24비트의 곱을 계산하고 결과의 최하위 32비트를 제공합니다. 더블 엑스xyyy 의 최상위 8비트는

__[u]mulhi(x,y)정수 매개변수 xx를 계산합니다.xyyy를 입력하고 64비트 결과 중 가장 중요한 32비트를 제공합니다.

__[u]mul64hi(x,y)64비트 정수 매개변수 xx 계산xyyy를 입력하고 128비트 결과의 최상위 64비트를 제공합니다.

__saturate(x)xx 라면x 가 0보다 작으면 0을 반환합니다.xxx가 1보다 크면 1을 반환하고, x가 [0, 1] 사이에 있으면xx를엑스

__[u]sad(x,y,z)(Sum of Absolute Difference )정수 매개변수 zz 찾기z 및 정수 매개변수xxxyyy 차이 의 절대값의 합

__clz(x)32비트 정수 매개변수 xx를 계산합니다.x 의 선행 0

__clzll(x)64비트 정수 매개변수 xx 계산x 의 선행 0

__ffs(x)정수 매개변수 x의 첫 번째 1비트 위치를 반환합니다. x가 0이면 최하위 비트 위치는 1입니다.

__ffs()0을 반환합니다. 이것은 리눅스 함수 ffs와 동일합니다.

__ffsll(x)64비트 정수 매개변수 xx를 반환합니다.x 의 첫 번째 비트 위치는x가 0이면 최하위 비트 위치는 1입니다.

__ffsll()0을 반환합니다. 이는 Linux 함수 ffsll과 동일합니다.

时间函数 

clock_t clock();매 클럭 사이클마다 증가하는 카운터의 반환 값

kernel시작과 끝 에서 이 카운터를 샘플링하고 두 샘플의 차이를 취합니다.

그리고 스레드당 클럭 사이클마다 장치를 통해 스레드를 완전히 실행하여 얻은 결과를 기록합니다.

스레드의 명령을 실행하는 장치에서 실제로 소비한 클록 주기 수가 아닌

스레드가 시간 세그먼트로 잘리기 때문에 전자의 숫자가 후자보다 큽니다.

纹理类型

CUDAGPU그래픽용 텍스처 메모리를 사용하여 하드웨어 텍스처 렌더링의 하위 집합을 지원합니다.

텍스처 메모리를 통해 데이터를 읽으면 전역 메모리에 비해 많은 성능 이점이 있습니다.

텍스처 메모리는 texture fetches다음과 같은 장치 기능을 통해 kernel읽혀집니다.

Texture fetch의 첫 번째 매개변수는 다음과 같은 texture referece객체를 지정합니다.

Texture reference텍스처 메모리의 어느 부분이fetch

사용 되기 전에 호스트의 기능에 의해 일부 메모리 영역에 바인딩되어야 kernel합니다.runtime

일부는 texture reference동일한 텍스처 또는 텍스처 매핑된 메모리에 바인딩될 수 있습니다.

Texture reference몇 가지 속성이 있습니다

그 중 하나는 텍스처가 텍스처 좌표를 통해 주소 지정하는 1차원 배열을 사용하는지 여부를 지정할 수 있다는 것입니다.

또는 텍스처가 두 개의 텍스처 좌표로 주소 지정하는 2차원 배열을 사용하는지 여부를 지정합니다.

texels배열의 요소는 간단히 (텍스처 요소) 라고 합니다.

fetch또 다른 속성은 텍스처의 입력 및 출력 데이터 유형을 정의하는 것입니다.

Texture Reference 声明

일부 texture reference속성은 고정되어 있으며 texture reference. 변수 는 texture reference파일 범위에서 유형으로 texture선언됩니다.

Texrure<Type, Dim, ReadMode> texRef;

~에

  • Type텍스처를 선택할 때 지정된 데이터 유형이 반환됩니다.
  • Dimtexture reference1 또는 2와 같은 차원을 지정합니다 . Dim기본값은 1인 선택적 인수입니다.
  • ReadModecudaReadModeNormalizedFloat또는 와 같습니다 . 또는 정수 유형인 경우 cudaReadModeElementType반환 된 값은 실제로 정수 유형이 매핑되는 부동 소수점 유형으로 처리됩니다 . 예를 들어 부호 없는 8비트 텍셀이 있는 값은 다음과 같이 읽습니다. 0xff ; 그렇다면 변환이 수행되지 않습니다. 기본값 은 선택적 인수 입니다.cudaReadModeNormalizedFloatType16-bit8-bitunsigned[0.0,1.0]signed[-1.0,1.0]1cudaReadModeElementTypeReadModecudaReadModeElementType
Runtime Texture Reference 属性 

일부 texture reference속성은 고정되어 있지 않으며 호스트 런타임에 따라 변경될 수 있습니다.

normalized텍스처 좌표가 , 주소 지정 모드 및 텍스처 필터링 인지 여부를 지정할 수 있습니다 .

기본적으로 텍스처는 좌표 를 기준으로 공간 내 텍스처 크기인 [0,N)부동 소수점 좌표로 참조 됩니다.N

예: 64x32크기의 텍스처에는 x축 [0,63]과 y축 좌표 범위가 있습니다.[0,31]

Normalized텍스처는 [0.0,1.0)대신 좌표로 참조됩니다.[0,N)

따라서 동일한 텍스처가 X축 과 Y축을 64x32가리킬 것입니다.normalized[0.0,1.0)[0.0,1.0)

Normalized텍스처 좌표는 본질적으로 일부 애플리케이션에 적합합니다. 예를 들어 텍스처 좌표는 텍스처 크기와 무관합니다.

주소 지정 모드는 텍스처 좌표가 범위를 벗어날 때 발생하는 상황을 정의합니다.

텍스처 좌표를 사용할 때 unnormalized텍스처 좌표가 범위를 벗어날 경우 [0, N)0보다 작은 값은 0으로 설정되고, N보다 큰 값은 N-1로 설정된다.

의 텍스처 좌표를 사용하는 경우 normalized텍스처 좌표 범위는 으로 제한됩니다 [0.0,1.0). 의 텍스처 좌표 에 대해 주소 normalized지정도 지정됩니다.warp

warp주소 지정은 일반적으로 텍스처 좌표의 소수 부분에만 작용하는 주기적 신호가 텍스처에 포함되어 있을 때 사용됩니다.

예를 들어 1.25는 0.25로 처리되고 -1.25는 0.75로 처리됩니다.

선형 텍스처 필터링은 텍스처가 부동 소수점 데이터를 반환하도록 설정된 경우에만 사용할 수 있습니다.

텍스처 가져 오기 주소를 읽을 인접 텍셀 texel에서 정밀도가 낮은 보간을 수행합니다.

그리고 texel텍스처 좌표를 기반으로 보간된 텍스처 가져오기 값을 반환합니다.

1D 텍스처에서 단순 보간을 수행하고, bilinear2D 텍스처에서 보간을 수행합니다.

线性内存纹理操作对比CUDA 数组

텍스처는 선형 메모리 또는 CUDA 배열에 할당될 수 있으며 텍스처는 선형 메모리에 할당됩니다.

  • 차원이 1인 경우에만 해당됩니다.
  • 텍스처 필터링은 지원되지 않습니다.
  • 정규화되지 않은 텍스처 좌표 주소 지정만 사용할 수 있습니다.
  • 다양한 주소 지정 모드를 지원할 수 없습니다. 범위를 벗어난 텍스처 액세스는 0을 반환합니다.

장치 런타임 구성 요소

장치 runtime구성요소는 장치 기능에서만 사용할 수 있습니다.

同步函数:void __syncthreads();

블록 내의 모든 스레드를 동기화합니다. 모든 스레드가 이 지점에 도달하면 정상적인 실행을 재개합니다.

__syncthreads()일반적으로 동일한 블록 간의 스레드 통신을 조정하는 데 사용됩니다.

블록 내의 일부 스레드가 동일한 공유 또는 전역 메모리에 액세스하는 경우

일부 메모리 액세스에 잠재적으로 위험 할 수 있습니다 read-after-write, write-after-read.write-after-write

스레드 간 액세스를 동기화하면 이러한 데이터 위험을 피할 수 있습니다.

__syncthreads()조건부 코드에 배치하는 것은 허용되지만 전체 스레드 블록의 조건이 전체적으로 동일한 경우에만 허용됩니다. 그렇지 않으면 코드 실행이 중단되거나 의도하지 않은 부작용이 발생할 수 있습니다.

类型转换函数

다음 함수의 접미사는 IEEE-754 반올림 모드를 지정합니다.

rn가장 가까운 짝수를 찾는 것입니다

rz0에 가깝다

ru반올림됩니다(양의 무한대로).

rd반내림(음의 무한대로)

int __float2int_[rn,rz,ru,rd](float);지정된 반올림 모드를 사용하여 부동 소수점 인수를 정수로 변환합니다.

Unsignde int __float2unit_[rn,rz,ru,zd](float);지정된 반올림 모드를 사용하여 부동 소수점 인수를 부호 없는 정수로 변환합니다.

float __int2float_[rn,rz,ru,rd](int);지정된 반올림 모드를 사용하여 정수 인수를 부동 소수점 숫자로 변환합니다.

float __int2float_[rn,rz,ru,rd](unsigned int);지정된 반올림 모드를 사용하여 부호 없는 정수 인수를 부동 소수점 숫자로 변환합니다.

Type Casting 函数

float __int_as_float(int);정수 인수에서 부동 소수점의 유형 캐스팅을 수행하고 값은 변경되지 않습니다.

__int_as_float(0xC0000000)예 를 들어,-2

int __float_as_int(float);부동 소수점 인수에 대해 수행된 정수 유형 캐스팅으로 값은 변경되지 않습니다.

__float_as_int (1.0f)예 를 들어,0x3f800000

纹理函数
  1. 장치 메모리 텍스처 작업: 장치 메모리의 텍스처는 tex1Dfetch()함수를 통해 액세스됩니다.
template<class Type>
Type tex1Dfetch(
texture<Type, 1, cudaReadModeElementType> texRef,
int x);
float tex1Dfetch(
texture<unsigned char, 1, cudaReadModeNormalizedFloat> texRef,
int x);
float tex1Dfetch(
texture<signed char, 1, cudaReadModeNormalizedFloat> texRef,
int x);
float tex1Dfetch(
texture<unsigned short, 1, cudaReadModeNormalizedFloat> texRef,
int x);
float tex1Dfetch(
texture<signed short, 1, cudaReadModeNormalizedFloat> texRef,
int x);

이 함수는 텍스처 좌표를 통해 x바인딩된 선형 메모리의 texture reference texRef영역을 가져옵니다.

정수 유형의 경우 텍스처 필터링 및 주소 지정 모드 선택이 허용되지 않습니다. 32-bit이러한 함수의 경우 정수를 부동 소수점 으로 업그레이드해야 할 수도 있습니다.

아래 함수는 튜플 지원을 2-보여 줍니다.4-

float4 tex1Dfetch(
texture<uchar4, 1, cudaReadModeNormalizedFloat> texRef,
int x); 

텍스처 좌표로 바인딩된 선형 메모리 영역을 x선택합니다.texture reference texRef

  1. CUDA 배열 텍스처 작업: tex1D() 또는 tex2D() 함수를 통해 CUDA 배열의 텍스처에서 액세스
template<class Type, enum cudaTextureReadMode readMode>
Type tex1D(texture<Type, 1, readMode> texRef, float x);
template<class Type, enum cudaTextureReadMode readMode>
Type tex2D(texture<Type, 2, readMode> texRef, float x, float y); 

이 함수는 텍스처 좌표 x 및 y에 의해 CUDA바인딩된 배열의 texture reference texRef영역을 선택합니다.

Texture reference컴파일 시간(고정) 및 런타임(변수) 속성은 좌표 해석 방법, 텍스처를 가져올 때 발생하는 처리, 텍스처 가져오기에서 반환되는 값을 결정합니다.


원자 기능

원자 함수는 Compute Compatibility 1.1이 설치된 장치에서만 사용할 수 있습니다.

/*
1. atomicAdd()
从全局内存中读取地址为address 的32-bit 字old,计算(old + val),将结果返回全
局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old
*/

int atomicAdd(int* address, int val);
unsigned int atomicAdd(unsigned int* address, unsigned int val);
/*
2. atomicSub()
从全局内存中读取地址为address 的32-bit 字old,计算(old - val),将结果返回全
局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old
*/

int atomicSub(int* address, int val);
unsigned int atomicSub(unsigned int* address, unsigned int val);
/*
3 atomicExch()
从全局内存中读取地址为address 的32-bit 字old,存储val 返回全局内存中的同一地址。
这二个操作由一个原子操作执行。函数返回old。
*/

int atomicExch(int* address, int val);
unsigned int atomicExch(unsigned int* address, unsigned int val);
float atomicExch(float* address, float val);
/* 
4 atomicMin()
从全局内存中读取地址为address 的32-bit 字old,计算old 和val 的最小值,将结果返
回全局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old。
*/

int atomicMin(int* address, int val);
unsigned int atomicMin(unsigned int* address,
unsigned int val);
/* 
5 atomicMax()
从全局内存中读取地址为address 的32-bit 字old,计算old 和val 的最大值,将结果返
回全局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old
*/

int atomicMax(int* address, int val);
unsigned int atomicMax(unsigned int* address, unsigned int val);
/*
6 atomicInc()
从全局内存中读取地址为address 的32-bit 字old,计算((old >= val) ? 0 :
(old+1)),将结果返回全局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old
*/
unsigned int atomicInc(unsigned int* address, unsigned int val);
/*
7 atomicDec()
从全局内存中读取地址为address的32-bit 字old,计算(((old ==0)| (old > val)) ?
val : (old-1)), 将结果返回全局内存中的同一地址。这三个操作由一个原子操作执行。
函数返回old。
*/
unsigned int atomicDec(unsigned int* address, unsigned int val);
/*
8 atomicCAS()
从全局内存中读取地址为address 的32-bit 字old,计算(old == compare ? val :
old),将结果返回全局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old
(比较和置换)
*/
int atomicCAS(int* address, int compare, int val);
unsigned int atomicCAS(unsignedint*address, unsigned int compare, unsigned int val);
/* 
9. atomicAnd()
从全局内存中读取地址为address 的32-bit 字old,计算(old & val),将结果返回全
局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old。
*/
int atomicAnd(int* address, int val);
unsigned int atomicAnd(unsigned int* address, unsigned int val);
/* 10. 
atomicOr()
从全局内存中读取地址为address 的32-bit 字old,计算(old | val),将结果返回全
局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old。
*/ 
int atomicOr(int* address, int val);
unsigned int atomicOr(unsigned int* address, unsigned int val);
/* 
11 atomicXor()
从全局内存中读取地址为address 的32-bit 字old,计算(old ^ val),将结果返回全
局内存中的同一地址。这三个操作由一个原子操作执行。函数返回old
*/

int atomicXor(int* address, int val);
unsigned int atomicXor(unsigned int* address, unsigned int val);

원자 함수는 전역 메모리의 단어에 32-bit대해 원자 읽기-수정-쓰기 작업을 수행합니다.

예를 들어, atomicAdd()전역 메모리의 동일한 주소에 있는 워드를 읽고 32-bit, 여기에 정수를 추가하고, 결과를 다시 동일한 주소에 씁니다.

소위 "원자적"은 작업이 다른 스레드를 방해하지 않도록 보장하는 것입니다. 작업이 완료될 때까지 다른 스레드는 이 주소에 액세스할 수 없습니다.

원자 연산은 32-bit부호 있는 정수와 부호 없는 정수에만 사용할 수 있습니다.


호스트 런타임 구성요소

호스트 Runtime구성 요소는 호스트 기능에서만 사용할 수 있습니다.

다음과 같은 문제를 처리하는 기능을 제공합니다.

  • 장치 관리
  • 컨텍스트 관리
  • 메모리 관리
  • 인코딩 모듈 관리
  • 집행 통제
  • 텍스처 참조 관리
  • OpenGL과 Direct3D의 상호 운용성

이는 두 가지로 구성됩니다 API.

낮은 수준의 API호출 CUDA드라이버API

드라이버 위에서 실행되는 높은 수준의 API호출CUDA runtime APICUDAAPIAPI

이들은 API상호 배타적입니다. 애플리케이션은 그 중 하나를 선택하여 사용해야 합니다.

CUDA runtime고유한 초기화, 관리 및 모듈 관리 기능을 제공하여 context장치 코드 관리가 용이합니다.

Nvcc생성된 C호스트 코드는 를 기반으로 하므로 CUDA runtime이 코드에 연결하는 애플리케이션은 다음을 사용해야 합니다.CUDA runtime API

이와 대조적으로 CUDA드라이버에는 API더 많은 코드가 필요하므로 프로그래밍과 디버깅이 더 어려워지지만 객체
만 처리하므로 더 나은 제어 기능을 제공하고 언어 독립적 입니다.cubin

특히 CUDA드라이버 API구성 및 시작 의 경우 Kernel실행 구성 및 kernel매개변수가 실행 구성 구문을 대체하여 외부 함수 호출을 지정해야 하기 때문에 더 어렵습니다.

마찬가지로 장치 에뮬레이션은 CUDA드라이버 에서 API작동 하지 않습니다.

CUDA드라이버는 동적 라이브러리를 API통해 제공되며 cuda모든 진입점에는 다음이 붙습니다.cu

CUDA runtime API동적 라이브러리를 통해 제공되며 cudart모든 진입점에는 다음이 붙습니다.cuda


공공 개념

장비

둘 다 API시스템에서 사용 가능한 장치를 열거하고, 해당 속성을 쿼리하고, 실행할 장치 중 하나를 선택하는 기능을 제공합니다.kernel

일부 호스트 스레드는 동일한 장치에서 장치 코드를 실행할 수 있지만 설계상 호스트 스레드는 하나의 장치에서만 장치 코드를 실행할 수 있습니다.

따라서 다중 호스트 스레드는 여러 장치에서 장치 코드를 실행해야 합니다.

또한 하나의 호스트 스레드에서 runtime생성된 소스 파일은 CUDA다른 호스트 스레드에서 사용할 수 없습니다.

메모리

CUDA장치 메모리는 선형 메모리 또는 배열 로 할당 가능

장치의 선형 메모리는 32-bit주소 공간을 사용하므로 별도로 할당된 엔터티는 포인터를 통해 서로 참조할 수 있습니다.

예를 들어 이진 트리 구조에서 CUDA배열은 텍스처 가져오기에 최적화된 불투명 메모리 레이아웃으로, 각각 1, 2 또는 4개의 구성 요소를 포함하는 1차원 또는 2차원 요소로 구성되며 각 구성 요소는 부호가 있거나 부호가 없을 수 있습니다. 비트, 16비트 또는 32비트 정수, 16비트 부동 소수점(드라이버에서만 지원 ) CUDA또는 32비트 부동 소수점.
API

CUDA배열은 kernel텍스처를 통해서만 읽을 수 있습니다.

선형 메모리와 CUDA어레이 모두 호스트의 메모리 복사 기능으로 읽고 쓸 수 있습니다.

malloc()함수에 의해 할당된 pageable호스트 메모리 와 달리 호스트는 호스트 메모리를 할당하고 해제할 수 있는 함수 runtime도 제공합니다.page-locked

호스트 메모리가 다음과 같이 할당된 경우page-locked

메모리 사용 의 장점 page-locked은 호스트 메모리와 장치 메모리 사이의 대역폭이 매우 높다는 것입니다.

그러나 너무 많은 page-locked메모리를 할당하면 시스템에서 사용할 수 있는 물리적 메모리의 양이 줄어들어 전체 시스템 성능이 저하됩니다.

OpenGL Interoperability

OpenGL 버퍼 객체는 CUDA 주소 공간에 매핑되어 CUDA가 OpenGL이 쓴 데이터를 읽을 수 있습니다.

또는 CUDA를 활성화하여 OpenGL에서 사용되는 데이터를 쓸 수 있습니다.

Direct3D Interoperability

Direct3D 9.0 꼭지점 버퍼는 CUDA 주소 공간에 매핑되어 CUDA가 Direct3D에서 쓴 데이터를 읽을 수 있습니다.

또는 CUDA를 활성화하여 Direct3D에서 사용하는 데이터를 쓸 수 있습니다.

CUDA 컨텍스트는 호출할 시작/끝 함수를 포함하여 매번 하나의 Direct3D 장치만 사용할 수 있습니다.

CUDA 컨텍스트와 Direct3D 장치는 동일한 GPU에 구축되어야 합니다.

이는 CUDA 장치와 연결된 Direct3D 어댑터를 쿼리하여 확인할 수 있습니다.

Direct3D 장치는 D3DCREATE_HARDWARE_VERTEXPROCESSING 플래그를 사용하여 생성되어야 합니다.

CUDA는 아직 다음을 지원하지 않습니다.

  1. Direct3D 9.0 이외의 버전
  2. 꼭짓점 버퍼 이외의 Direct3D 개체
  3. cudaD3D9GetDevice() 및 cuD3D9GetDevice()는 Direct3D 장치 및 CUDA 컨텍스트가 Direct3D의 로딩 밸런스 및 CUDA의 상호 운용성과 같은 다양한 장치에 설정되도록 보장할 수도 있습니다.

비동기 동시 실행

호스트와 장치 간의 동시 실행을 용이하게 하기 위해 일부 런타임 기능은 비동기식입니다. 장치가 요청된 작업을 완료할 때까지 제어가 애플리케이션에 반환됩니다.

  • 커널은 __global__ 함수 또는 cuGridLaunch() 및 cuGridLaunchAsync()에 의해 시작됩니다.
  • 메모리 복사를 수행하는 함수에는 접미사 Async가 필요합니다.
  • 장치 간 메모리 복사를 수행하는 기능
  • 메모리 설정 기능

일부 장치는 페이지 잠금 호스트 메모리와 장치 메모리 간에 동시 복사를 수행할 수도 있습니다.

애플리케이션은 CU_DEVICE_ATTRIBUTE_GPU_OVERLAP과 함께 cuDeviceGetAttribute()를 통해 이 기능을 사용할 수 있는지 여부를 쿼리할 수 있습니다.

이 함수는 현재 메모리 복사만 지원하며, CUDA 배열이나 2D 배열을 제외하고 cudaMallocPitch() 또는 cuMemAllocPitch()를 통해 할당됩니다.

애플리케이션은 스트림을 통해 동시성을 관리합니다. 스트림은 순서대로 실행되는 일련의 작업입니다. 반면에, 다른 스트림은 다른 또는 병렬적인 비순차적 실행을 따를 수 있습니다.

스트림은 스트림 개체를 생성하고 해당 스트림 매개변수를 커널의 시작 시퀀스 및 호스트-장치 메모리 복사본에 지정하여 정의됩니다.

모든 작업이 완료된 경우에만: 포함된 작업은 스트림의 일부여야 하며 완료될 때까지 후속 작업이 완료되지 않습니다.

매개변수가 0인 스트림만 시작됩니다(예: 커널 시작, 메모리 설정 또는 메모리 복사).

cudaStreamQuery() 및 cuStreamQuery()는 애플리케이션이 스트림의 모든 작업이 완료되었는지 쿼리하는 메서드를 제공합니다.

cudaStreamSynchronize() 및 cuStreamSynchronize()는 스트림의 모든 작업이 완료될 때까지 애플리케이션이 대기하도록 강제하는 메서드를 제공합니다.

마찬가지로 cudaThreadSynchronize() 및 cuThreadSynchronize() 응용 프로그램은 모든 장치 작업이 완료될 때까지 런타임을 강제로 대기시킬 수 있습니다.

속도 저하를 방지하기 위해 이러한 기능은 타이밍 목적, 격리된 시작 또는 메모리 복사 오류에 사용하는 것이 가장 좋습니다.

런타임은 또한 장치의 프로세스를 보다 면밀히 모니터링하는 방법을 제공합니다.

정확한 시간에 애플리케이션이 프로그램의 어느 지점에서나 이벤트를 비동기적으로 기록하고 이러한 이벤트가 기록된 시기를 쿼리하도록 합니다.

서로 다른 스트림의 두 작업을 동시에 실행할 수 없습니다.

페이지 잠금 호스트 메모리 할당, 장치 메모리 할당, 장치 메모리 설정, 장치 간 메모리 복사 또는 이들 간의 이벤트 로깅 여부

시스템의 모든 CUDA 응용 프로그램에 대해 CUDA_LAUNCH_BLOCKING 환경 변수를 1로 설정하여 비동기 실행을 전역적으로 끌 수 있습니다.

이 기능은 디버깅 목적으로만 사용되며 소프트웨어 제품의 신뢰성을 높이기 위한 것이 아닙니다.


런타임 API

[초기화]

RuntimeAPI에 대한 초기화 함수는 명시적으로 없으며 첫 번째 런타임 함수 호출 시 초기화됩니다.

런타임 함수를 적시에 호출해야 하는 시점과 첫 번째 호출에서 런타임에 들어간 오류 코드를 언제 설명해야 하는지 유의해야 합니다.

[기기 관리]

cudaGetDeviceCount()cudaGetDeviceProperties()이러한 장치를 열거하고 해당 속성을 얻는 방법을 제공합니다 .

int deviceCount;
cudaGetDeviceCount(&deviceCount);
Int device;
for (device = 0; device < deviceCount; ++device) {
    
    
	cudaDeviceProp deviceProp;
	cudaGetDeviceProperties(&deviceProp,device);
}

cudaSetDevice()호스트 스레드와 연관된 장치를 선택하는 데 사용됩니다.

cudaSetDevice(device); 

__global__ 함수를 호출하기 전에 장치를 선택해야 합니다.

그렇지 않으면 장치 0이 자동으로 선택되고 이후의 모든 장치 선택은 유효하지 않습니다.

[메모리 관리]

장치 메모리를 할당 및 해제하고, 전역 메모리에서 임의로 선언된 변수에 의해 할당된 메모리에 액세스하고, 호스트 메모리에서 장치 메모리로 데이터를 전송하는 함수를 호출합니다.

cudaMalloc()선형 메모리는 또는 를 통해 할당되고 , 다음 cudaMallocPitch()을 통해 cudaFree()해제됩니다.

다음 코드는 선형 메모리에 256개의 부동 소수점 요소 배열을 할당하는 방법을 보여줍니다.

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

cudaMallocPitch()행 주소에 액세스하거나 2D 배열을 장치 메모리의 다른 영역에 복사할 때 최상의 성능을 보장하려면 2D 배열을 할당하는 것이 좋습니다.

반환된 값은 pitch배열 요소에 액세스하는 데 사용해야 합니다.

다음 코드는 부동 소수점을 사용하여 너비 x 높이 2D 배열을 할당하는 방법과 장치 코드에서 배열 요소를 반복하는 방법을 보여줍니다.

// host code
float* devPtr;
int pitch;
cudaMallocPitch((void**)&devPtr, &pitch,
width*sizeof(float),height);
myKernel<<<100,512>>>(devPtr,pitch);
// device code
__global__ void myKernel(float* devPtr, int pitch)
{
    
    
	for (int r = 0; r < height; ++r) {
    
    
		float* row = (float*)((char*)devPtr + r * pitch);
		for (int c = 0; c < width; ++c) {
    
    
			float element = row[c];
		 }
	}
} 

cudaMallocArray()CUDA 배열은 할당, cudaFreeArray()할당 해제를 통해 할당됩니다.

cudaMallocArray()형식 해석이 필요합니다 cudaCreateChannelDesc().

다음 코드는 32비트 부동 소수점 숫자를 사용하여 너비 x 높이의 2D 배열을 할당하는 방법을 보여줍니다.

cudaChannelFormatDescchannelDesc=
cudaCreateChannelDesc<float>();
cudaArray* cuArray;
cudaMallocArray(&cuArray, &channelDesc, width, height);

cudaGetSymbolAddress()전역 메모리에서 선언된 변수에 할당된 메모리 주소를 얻는 데 사용됩니다.

할당된 메모리의 크기는 다음과 같이 cudaGetSymbolSize()얻습니다 .

cudaMalloc()할당된 선형 메모리
cudaMallocPitch()할당된 선형 메모리, CUDA 배열, 전역 변수 할당 메모리 또는 상주 메모리

다음 코드는 이전 예제에서 할당된 CUDA 배열에 2D 배열을 복사하는 방법을 보여줍니다.

cudaMemcpy2DToArray(cuArray, 0, 0, devPtr, pitch,
width * sizeof(float),height,
cudaMemcpyDeviceToDevice);

다음 코드는 일부 호스트 메모리 배열을 장치 메모리에 복사하는 방법을 보여줍니다.

float data[256];
int size = sizeof(data);
float* devPtr;
cudaMalloc((void**)&devPtr, size);
cudaMemcpy(devPtr, data, size, cudaMemcpyHostToDevice);

다음 코드는 일부 호스트 메모리 배열을 상주 메모리에 복사하는 방법을 보여줍니다.

__constant__ float constData[256];
float data[256];
cudaMemcpyToSymbol(constData, data, sizeof(data));

[스트림 관리]
스트림을 생성 및 소멸하고 스트림의 모든 작업이 완료되었는지 확인하는 함수를 호출합니다.

다음 코드는 두 개의 스트림이 생성되는 것을 보여줍니다.

cudaStream_t stream[2];
for(inti=0;i<2;++i)
cudaStreamCreate(&stream[i]);

다음 코드는 각 스트림이 호스트에서 장치로 메모리 복사, kernel시작, 장치에서 호스트로 메모리 복사 등 한 번 정의되고 실행됨을 보여줍니다.

for (int i = 0; i < 2; ++i)
	cudaMemcpyAsync(inputDevPtr+i*size,hostPtr+i*size,size,cudaMemcpyHostToDevice,stream[i]);
for (int i = 0; i < 2; ++i)
	myKernel<<<100, 512, 0, stream[i]>>>(outputDevPtr + i * size, inputDevPtr + i * size, size);
for (int i = 0; i < 2; ++i)
	cudaMemcpyAsync(hostPtr + i * size, outputDevPtr + i * size, size, cudaMemcpyDeviceToHost, stream[i]);
cudaThreadSynchronize();

각 스트림은 입력 배열의 일부를 hostPtr장치 메모리의 입력 배열에 복사합니다.inputDevPtr

결과를 호출 하고 동일한 섹션 에 복사하여 myKernel()장치에서 처리됩니다.inputDevPtroutputDevPtrhostPtr

두 개의 스트림을 처리하면 hostPtr한 스트림에서 다른 스트림으로 메모리 복사본을 오버레이할 수 있습니다. 모든 오버레이의 경우 호스트 메모리를 hostPtr가리켜야 합니다 .page-locked

float*hostPtr;
cudaMallocHost((void**)&hostPtr,2*size;

cudaThreadSynchronize()처리를 계속하기 전에 모든 스트림이 완료되었는지 확인하기 위해 마지막에 호출됩니다.

[이벤트 관리]

호출 함수는 이벤트를 생성, 기록 및 삭제하는 데 사용되며 두 이벤트 사이에 소요된 시간을 쿼리할 수 있습니다.

다음 코드는 두 개의 이벤트가 생성되는 것을 보여줍니다.

cudaEvent_tstart,sto;
cudaEventCreate(&stat);
cudaEventCreate(&stop);

이러한 이벤트를 사용하여 이전 코드의 시간을 측정할 수 있습니다.

cudaEventRecord(start, 0);
for (int i = 0; i < 2; ++i)
	cudaMemcpyAsync(inputDev + i * size, inputHost + i * size, size, cudaMemcpyHostToDevice, stream[i]);
for (int i = 0; i < 2; ++i)
	myKernel<<<100, 512, 0, stream[i]>>> (outputDev + i * size, inputDev + i * size, size);
for (int i = 0; i < 2; ++i)
	cudaMemcpyAsync(outputHost + i * size, outputDev + i * size, size, cudaMemcpyDeviceToHost, stream[i]);
cudaEventRecord(stop, 0);
cudaEventSynchronize(stop);
float elapsedTime;
cudaEventElapsedTime(&elapsedTime, start, stop);

[텍스처 참조 관리]

관리를 위한 호출 기능texture reference

텍스처 유형은 상위 수준 API에 의해 정의된 노출된 구조이고 texture Reference유형은 하위 수준 API에 의해 정의됩니다. 예를 들면 다음과 같습니다.

struct textureReference
{
    
    
int normalized;
enum cudaTextureFilterMode filterMode;
enum cudaTextureAddressMode addressMode[2];
struct cudaChannelFormatDesc channelDesc;
}

normalized텍스처 좌표가 다음인지 여부를 지정합니다 normalized.

0이 아닌 경우 텍스처의 모든 요소는 또는 [0,1]대신 텍스처 좌표를 갖습니다 . 여기서 및 는 텍스처의 크기입니다.[0,width-1][0,height-1]widthheight

filterMode필터 모드를 지정합니다. 입력 텍스처 좌표를 기반으로 텍스처에서 선택한 값이 반환되는 방식을 계산합니다.

필터 모드는 cudaFilterModePoint또는 cudaFilterModeLinear;

그렇다면 cudaFilterModePoint반환된 값은 입력 텍스처 좌표에 가장 가까운 값과 같습니다 texel.

그렇다면 반환된 값은 cudaFilterModeLinear가장 가까운 입력 텍스처 좌표 중 2개 texel(1D 텍스처의 경우) 또는 4개 texel(2D 텍스처의 경우) 를 선형 보간한 결과와 같습니다.

cudaFilterModeLinear반환 값은 부동 소수점 유형이어야 합니다.

addressMode주소 지정 모드 지정: 범위를 벗어난 텍스처 좌표를 제어하는 ​​경우 첫 번째 텍스처 좌표의 주소 지정 모드와 두 번째 텍스처 좌표의 주소 지정 모드를 각각 지정합니다.

cudaAddressModeClamp주소 지정 모드는 또는 와 같습니다 cudaAddressModeWrap.

yes 인 경우 cudaAddressModeClamp범위를 벗어난 텍스처 좌표가 유효한 범위로 고정됩니다.

yes 인 경우 cudaAddressModeWrap범위를 벗어난 텍스처 좌표를 유효한 범위로 덮어씁니다.

cudaAddressModeWrap에 대한 텍스처 좌표 만 normalized지원됩니다.

channelDesc텍스처를 선택할 때 반환 값의 형식을 정의합니다. 다음 코드를 참조하세요.

struct cudaChannelFormatDesc {
    
    
	int x, y, z, w;
	enum cudaChannelFormatKind f;
};

~에

x,y,zw반환 값의 각 부분에 있는 비트 수입니다 .

f예:

  • cudaChannelFormatKindSigned이 부분이 부호 있는 정수인 경우,
  • cudaChannelFormatKindUnsigned부호 없는 정수인 경우
  • cudaChannelFormatKindFloat수레라면.

normalized,addressMode,filterMode호스트 코드에서 직접 수정할 수 있습니다 .

CUDA 배열 바인딩에만 적용됩니다.texture reference

커널이 다음을 통해 texture reference텍스처 메모리를 읽기 전에

texture referencecudaBindTexture()또는 를 사용하여 cudaBindTextureToArray()텍스처 에 바인딩해야 합니다 .

다음 코드는 가 가리키는 선형 메모리 texture reference에 a 를 바인딩하는 방법을 보여줍니다.devPtr

저수준 API 사용

texture<float, 1, cudaReadModeElementType> texRef;
textureReference* texRefPtr;
cudaGetTextureReference(&texRefPtr, “texRef”);
cudaChannelFormatDesc channelDesc = cudaCreateChannelDesc<float>();
cudaBindTexture(0, texRefPtr, devPtr, &channelDesc, size);

고급 API 사용

texture<float, 1, cudaReadModeElementType> texRef;
cudaBindTexture(0, texRef, devPtr, size);

다음 코드는 a를 texture referenceCUDA 배열 cuArray에 바인딩하는 방법을 보여줍니다.

하위 수준 API를 사용합니다.

texture<float, 2, cudaReadModeElementType> texRef;
textureReference* texRefPtr;
cudaGetTextureReference(&texRefPtr, “texRef”);
cudaChannelFormatDesc channelDesc;
cudaGetChannelDesc(&channelDesc, cuArray);
cudaBindTextureToArray(texRef, cuArray, &channelDesc);

고급 API 사용:

texture<float, 2, cudaReadModeElementType> texRef;
cudaBindTextureToArray(texRef, cuArray);

텍스처 참조에 텍스처를 바인딩하는 형식은 텍스처 참조를 선언할 때 매개변수와 일치해야 합니다.

그렇지 않으면 텍스처 가져오기 결과가 정의되지 않습니다.

cudaUnbindTexture()에 대한 바인딩을 제거하는 데 사용됩니다 texture reference.

[OpenGL 상호 운용성]

OpenGL 상호 운용성을 제어하는 ​​함수 호출

버퍼 객체는 매핑되기 전에 CUDA에 등록되어야 합니다.

사용 cudaGLRegisterBufferObject():

GLuint bufferObj;
cudaGLRegisterBufferObject(bufferObj);

kenrel등록 후에는 장치 메모리를 사용하여 버퍼 개체를 읽거나 쓸 수 있으며 , 메모리 장치의 주소는 cudaGLMapBufferObject()다음과 같이 반환됩니다.

GLuint bufferObj;
float* devPtr;
cudaGLMapBufferObject((void**)&devPtr, bufferObj);

cudaGLUnmapBufferObject()다음을 통해 매핑 및 등록을 언로드하고cudaGLUnregisterBufferObject()

[Direct3D 상호 운용성]

Direct3D 상호 운용성을 제어하는 ​​호출 함수

Direct3D상호 운용성은 다음에 의해 cudaD3D9Begin()시작되고 cudaD3D9End()종료되어야 합니다.

정점 객체는 매핑되기 전에 CUDA에 등록되어야 합니다. 사용 cudaD3D9RegisterVertexBuffer():

LPDIRECT3DVERTEXBUFFER9 vertexBuffer;
cudaD3D9RegisterVertexBuffer(vertexBuffer);

등록 후에는 kenrel을 통해 장치 메모리를 사용하여 버퍼 개체를 읽거나 쓸 수 있으며, 메모리 장치의 주소는 cudaD3D9MapVertexBuffer()다음과 같이 반환됩니다.

LPDIRECT3DVERTEXBUFFER9 vertexBuffer;
float* devPtr;
cudaD3D9MapVertexBuffer((void**)&devPtr, vertexBuffer);

cudaD3D9UnmapVertexBuffer()다음을 통해 매핑 및 등록을 언로드하고cudaD3D9UnregisterVertexBuffer()

[디버깅을 위해 장치 에뮬레이션을 사용]

프로그래밍 환경은 장치에서 실행되는 코드의 기본 디버깅을 지원하지 않지만 디버깅 목적을 위한 장치 에뮬레이션 모드가 함께 제공됩니다.

이 모드에서 애플리케이션을 컴파일할 때(- deviceemu옵션 사용) 장치 코드는 다음을 위해 컴파일됩니다.

개발자가 호스트의 기본 디버깅 지원을 사용하여 애플리케이션을 디버깅할 수 있습니다.

전처리기 매크로는 __DEVICE_EMULATION__이 모드에서 정의됩니다.

사용된 라이브러리를 포함하여 현재 애플리케이션의 모든 코드는 장치 에뮬레이션 또는 장치 실행을 위해 컴파일되어야 합니다.

연결된 장치 에뮬레이션이나 장치에서 실행된 코드로 인해 생성된 런타임 오류는 cudaErrorMixedDeviceExxcution에서 반환할 수 있습니다.

장치 에뮬레이션 모드에서 애플리케이션을 실행할 때 프로그래밍 모델은 런타임에 의해 에뮬레이션됩니다.

스레드 블록의 각 스레드에 대해 runtime스레드가 호스트에 생성됩니다. 개발자는 다음을 결정해야 합니다.

호스트가 블록당 실행할 수 있는 최대 스레드 수와 기본 스레드 1개

모든 스레드를 실행하는 데 충분한 메모리를 사용할 수 있으며 각 스레드에는 256 KB스택이 필요합니다.

장치 에뮬레이션 모드를 통해 사용할 수 있는 다양한 기능은 매우 효과적인 디버깅 도구 세트를 만듭니다.

호스트의 기본 디버깅 지원을 사용하면 개발자는 중단점 설정 및 데이터 검사와 같은 디버거의 모든 기능을 사용할 수 있습니다.

장치 코드는 호스트에서 실행되도록 컴파일되므로 이 코드는 장치에서 실행될 수 없는 코드로 보완될 수 있습니다.

printf()파일이나 화면( 등)에 대한 입력 및 출력 작업 과 같습니다 .

모든 데이터가 호스트에 상주하므로 모든 장치 또는 호스트 관련 데이터를 장치 또는 호스트 코드에서 읽을 수 있습니다.

마찬가지로 모든 장치 또는 호스트 기능은 장치 또는 호스트 코드에서 호출될 수 있습니다.

잘못된 동기화 사용을 방지하기 위해 runtime교착 상태 상태가 모니터링됩니다.

개발자는 장치 에뮬레이션 모드가 장치를 에뮬레이션하는 것이 아니라 모방한다는 점을 명심해야 합니다.

따라서 장치 에뮬레이션 모드는 알고리즘 오류를 찾는 데 매우 유용하지만 일부 오류는 찾기 어렵습니다.

그리드 내의 여러 스레드가 동시에 메모리 셀에 액세스하는 경우 장치 에뮬레이션 모드에서 실행할 때의 결과는 장치에서 실행될 때와 상당히 다릅니다. 스레드가 에뮬레이션 모드에서 순차적으로 실행되기 때문입니다.

호스트의 전역 메모리 또는 장치의 호스트 메모리에 대한 참조를 역참조할 때 장치 실행은 정의되지 않은 방식으로 거의 확실하게 실패하는 반면 장치 에뮬레이션은 올바른 결과를 생성할 수 있습니다.

대부분의 경우 동일한 부동 소수점 계산은 장치 에뮬레이션 모드를 통해 호스트에서 수행되는 것과 정확히 동일한 결과를 생성하지 않습니다.

이는 일반적으로 동일한 부동 소수점 계산에 대해 다른 컴파일러, 다른 명령어 세트 또는 다른 아키텍처는 말할 것도 없고 다른 컴파일러 옵션에 의해 얻은 다른 결과가 형성되는 것으로 예상됩니다.

특히 일부 호스트 플랫폼은 확장형 레지스터에 단정밀도 부동 소수점 계산의 중간 결과를 저장하므로 종종 장치 에뮬레이션 모드에서 정밀도에 큰 차이가 발생합니다.

이러한 상황이 발생하면 개발자는 다음을 시도할 수 있지만 어느 것도 작동이 완전히 보장되지는 않습니다.

  1. 불안정성으로 인해 단정밀도 저장을 강제하도록 일부 부동 소수점 변수를 선언합니다.
  2. gcc컴파일러 옵션을 사용하십시오 –ffloat-storegcc.
  3. Visual C++컴파일러의 /Op또는 옵션을 사용하십시오 /fp.
  4. Linux사용하기 위해 _FPU_GETCW()그리고 사용하기 _FPU_SETCW()위해Windows_controlfp()

코드의 일부가 단정밀도 부동 소수점 계산을 수행하도록 하는 함수

unsigned int originalCW;
_FPU_GETCW(originalCW);
unsigned int cw = (originalCW & ~0x300) | 0x000;
_FPU_SETCW(cw);
unsigned int originalCW = _controlfp(0, 0);
_controlfp(_PC_24, _MCW_PC);

처음에는 제어 워드의 현재 값을 저장하고 가수를 강제로 24비트로 저장 _FPU_SETCW(originalCW);하거나_controlfp(originalCW, 0xfffff);

컴퓨팅 장치와 달리 호스트 플랫폼은 일반적으로 정규화된 숫자도 지원합니다.

이로 인해 장치 에뮬레이션 모드와 장치 실행 모드 간에 결과가 크게 달라질 수 있습니다. 일부 계산에서는 한 경우에는 유한한 결과가 생성되고 다른 경우에는
무한한 결과가 생성될 수 있기 때문입니다.


드라이버 API

드라이버는 API핸들 기반이며 필수적입니다 API. 대부분의 개체는 불투명 핸들을 통해 참조됩니다.

사용 가능한 개체는 CUDA표 4-1에 요약되어 있습니다.
여기에 이미지 설명을 삽입하세요

[초기화]

cuInit()다른 함수가 호출되기 전에 함수 초기화 가 필요합니다 .

[기기 관리]

현재 시스템의 장치를 관리하는 함수를 호출합니다.

cuDeviceGetCount()cuDeviceGet()이러한 장치를 열거하는 데 사용됩니다 .

int deviceCount;
cuDeviceGetCount(&deviceCount);
int device;
for (int device = 0; device < deviceCount; ++device) {
    
    
CUdevice cuDevice;
cuDeviceGet(&cuDevice, device)
int major, minor;
cuDeviceComputeCapability(&major, &minor, cuDevice);
}

[컨텍스트 관리]

생성, 묶음, 분리를 위한 함수 호출CUDA context

A 는 손잡이 CUDA context와 비슷하다.CPU

Compute API 내에서 수행되는 모든 리소스와 작업은 에 캡슐화되며 시스템은 이러한 리소스가 파기될 때 자동으로 정리합니다 CUDA context.context

모듈 및 텍스처 참조와 같은 객체를 제외하고 각각은 context자체 별도의 32bit주소 공간을 가집니다.

따라서 서로 다른 값은 서로 다른 메모리 위치를 CUDA context참조합니다 .CUdeviceptr

Context호스트 스레드에는 일대일 대응 메커니즘이 있습니다.

호스트 스레드는 한 번에 하나의 장치만 가질 수 있습니다.context

a가 context생성 되면 cuCtxCreate()현재 호출하는 호스트 스레드가 됩니다.

현재 스레드가 유효하지 않은 경우 (장치 열거 또는 관리를 제외한 대부분의 기능 ) 에서 context작동하는 기능이 반환됩니다.CUDAcontextCUDA_ERROR_INVALID_CONTEXTcontext

context동일한 에서 실행되는 타사 라이센스 코드 간의 상호 운용성을 촉진하기 위해 드라이버는 API정의된 각 클라이언트에서 제공하는 context사용 카운터를 제공합니다.

예를 들어 동일한 라이브러리를 사용하여 세 개의 라이브러리를 로드하는 경우 CUDA context각 라이브러리는 cuCtxAttach()사용량 카운터를 증가시키기 위해 호출해야 하며, 라이브러리가 context사용을 마치면 cuCtxDetach()사용량 카운터를 감소시키기 위해 호출해야 합니다.

사용량 카운터가 0이 되면 context폐기됩니다.

대부분의 라이브러리의 경우 애플리케이션은 라이브러리를 로드하거나 초기화하기 전에 라이브러리를 생성해야 합니다 CUDA context.

이런 방식으로 애플리케이션은 자체적으로 사용할 라이브러리를 생성할 수 있으며 context, 라이브러리는 단순히 context자신에게 맡겨진 작업에 따라 작동합니다.

[모듈 관리]

호출 함수는 모듈을 로드 및 언로드하고 모듈의 핸들, 변수 포인터 또는 함수 정의를 얻는 데 사용됩니다.

Windows모듈은 에서와 같이 장치 코드와 데이터를 포함하는 동적으로 로드 가능한 타르볼이며 다음을 DLL통해 nvcc내보내집니다 .

함수, 전역 변수, 텍스처 참조를 포함한 모든 플래그의 이름은 모듈 범위에서 제공되므로 독립적인 제3자가 작성한 모듈이 동일 CUDA context하게

다음 코드는 모듈을 로드하고 kernel핸들을 가져오는 방법을 보여줍니다.

CUmodule cuModule;
cuModuleLoad(&cuModule, "myModule.cubin");
CUfunction cuFunction;
cuModuleGetFunction(&cuFunction, cuModule, “myKernel”);

[실행 제어]

장치에서 kernel실행을 관리하는 함수 호출

cuFuncSetBlockShape()주어진 함수의 각 블록에 있는 스레드 수와 스레드 ID 할당 방법을 설정하는 데 사용됩니다.

cuFuncSetSharedSize()함수의 공유 메모리 크기를 지정합니다.

cuParam*()함수 세트는 커널이 kernel포함 culanuchGrid()하거나 포함할 매개변수를 제공합니다.cuLanuch()

cuFuncSetBlockShape(cuFunction, blockWidth, blockHeight, 1);
int offset = 0; int i;
cuParamSeti(cuFunction, offset, i);
Offset += sizeof(i);
float f;
cuParamSetf(cuFunction, offset, f);
offset += sizeof(f);
char data[32];
cuParamSetv(cuFunction, offset, (void*)data, sizeof(data));
offset += sizeof(data);
cuParamSetSize(cuFunction, offset);
cuFuncSetSharedSize(cuFunction, numElements * sizeof(float));
cuLaunchGrid(cuFuntion, gridWidth, gridHeight);

[메모리 관리]

장치 메모리를 할당 및 해제하고 호스트와 장치 메모리 간에 데이터를 전송하기 위해 함수가 호출됩니다.

선형 메모리는 릴리스 에 의해 할당되거나 cuMemAlloc()릴리스 됩니다.cuMemAllocPitch()cuMemFree()

다음 코드는 선형 메모리에 256개의 부동 소수점 요소 배열을 할당하는 방법을 보여줍니다.

CUdeviceptr devPtr;
cuMemAlloc(&devPtr, 256 * sizeof(float));

행 주소에 액세스하거나 장치 메모리의 다른 영역에 배열을 복사할 때 최상의 성능을 보장하려면 배열을 할당하는 것이 좋습니다 2D.cuMemMallocPitch()2D

반환된 값은 pitch배열 요소에 액세스하는 데 사용해야 합니다.

다음 코드는 부동 소수점을 사용하여 너비 x 높이 2D 배열을 할당하는 방법과 장치 코드에서 배열 요소를 반복하는 방법을 보여줍니다.

// host code
CUdeviceptr devPtr;
int pitch;
cuMemAllocPitch(&devPtr, &pitch, width * sizeof(float), height, 4);
cuModuleGetFunction(&cuFunction,cuModule,"myKernel");
cuFuncSetBlockShape(cuFunction, 512, 1, 1);
cuParamSeti(cuFunction, 0, devPtr);
cuParamSetSize(cuFunction, sizeof(devPtr));
cuLaunchGrid(cuFunction, 100, 1);
// device code
__global__ void myKernel(float* devPtr)
{
    
    
	for (int r = 0; r < height; ++r) {
    
    
		float* row = (float*)((char*)devPtr + r * pitch);
		for (int c = 0; c < width; ++c) {
    
    
			float element = row[c];
		}
	}
}

CUDA배열은 cuArrayCreate()다음에 의해 할당되고 cuArrayDestroy()해제 됩니다.

다음 코드는 32-bitfloat를 사용하여 너비 x 높이 배열을 할당하는 방법을 보여줍니다 CUDA.

CUDA_ARRAY_DESCRIPTOR desc;
desc.Format = CU_AD_T_FLOAT;
desc.NumChannels = 1;
desc.Width = width;
desc.Height = height;
CUarray cuArray;
cuArrayCreate(&cuArray, &desc);

다음 코드는 이전 예제에서 할당된 CUDA 배열에 2D 배열을 복사하는 방법을 보여줍니다.

CUDA_MEMCPY2D copyParam;
memset(&copyParam, 0, sizeof(copyParam));
copyParam.dstMemoryType = CU_MEMORYTYPE_ARRAY;
copyParam.dstArray = cuArray;
copyParam.srcMemoryType = CU_MEMORYTYPE_DEVICE;
copyParam.srcDevice = devPtr;
copyParam.srcPitch = pitch;
copyParam.WidthInBytes = width * sizeof(float);
copyParam.Height = height;
cuMemcpy2D(&copyParam);

다음 코드는 일부 호스트 메모리 배열을 장치 메모리에 복사하는 방법을 보여줍니다.

float data[256];
int size = sizeof(data);
CUdeviceptr devPtr;
cuMemMalloc(&devPtr, size);
cuMemcpyHtoD(devPtr, data, size);

[스트림 관리]

호출 함수는 스트림을 생성 및 삭제하고 스트림의 모든 작업이 완료되었는지 확인하는 데 사용됩니다.

다음 코드는 두 개의 스트림이 생성되는 것을 보여줍니다.

CUStream stream[2];
for (int i = 0; i < 2; ++i)
cuStreamCreate(&stream[i], 0);

다음 코드는 각 스트림이 한 번 정의되고 실행된다는 것을 보여줍니다. 호스트에서 장치로 메모리 복사, 커널 시작, 장치에서 호스트로 메모리 복사

for (int i = 0; i < 2; ++i)
cuMemcpyHtoDAsync(inputDevPtr + i * size, hostPtr + i * size, size, stream[i]);
for (int i = 0; i < 2; ++i) {
    
    
	cuFuncSetBlockShape(cuFunction, 512, 1, 1);
	int offset = 0;
	cuParamSeti(cuFunction, offset, outputDevPtr);
	offset += sizeof(int);
	cuParamSeti(cuFunction, offset, inputDevPtr);
	offset += sizeof(int);
	cuParamSeti(cuFunction, offset, size);
	offset += sizeof(int);
	cuParamSetSize(cuFunction, offset);
}
cuLaunchGridAsync(cuFunction, 100, 1, stream[i]
for (int i = 0; i < 2; ++i)
	cuMemcpyDtoHAsync(hostPtr + i * size, outputDevPtr + i * size,
size, stream[i]);
cuCtxSynchronize();

각 스트림은 입력 배열의 일부를 hostPtr장치 메모리의 입력 배열에 복사하고 를 inputDevPtr호출하여 cuFunction장치에서 처리하며 inputDevPtr결과를 의 동일한 부분 outputDevPtr에 복사합니다.hostPtr

두 개의 스트림을 처리하면 hostPtr한 스트림에서 다른 스트림으로 메모리 복사본을 덮어쓸 수 있습니다.

재정의의 경우 다음 위치에서 호스트 메모리를 hostPtr가리켜야 합니다 .page-locked

float* hostPtr;
cuMemMallocHost((void**)&hostPtr, 2 * size);

cuCtxSynchronize()처리를 계속하기 전에 모든 스트림이 완료되었는지 확인하기 위해 마지막에 호출됩니다.

[이벤트 관리]

호출 함수는 이벤트를 생성, 기록 및 삭제하는 데 사용되며 두 이벤트 사이에 소요된 시간을 쿼리할 수 있습니다.

다음 코드는 두 개의 이벤트가 생성되는 것을 보여줍니다.

CUEvent start,
stop;cuEventCreate(&start);
cuEventCreate(&stop);

이러한 이벤트를 사용하여 이전 코드의 시간을 측정할 수 있습니다.

cuEventRecord(start, 0);
for (int i = 0; i < 2; ++i)
	cuMemcpyHtoDAsync(inputDevPtr + i * size, hostPtr + i * size, size, stream[i]);
for (int i = 0; i < 2; ++i) {
    
    
	cuFuncSetBlockShape(cuFunction, 512, 1, 1);
	int offset = 0;
	cuParamSeti(cuFunction, offset, outputDevPtr);
	offset += sizeof(int);
	cuParamSeti(cuFunction, offset, inputDevPtr);
	offset += sizeof(int);
	cuParamSeti(cuFunction, offset, size);
	offset += sizeof(int);
	cuParamSetSize(cuFunction, offset);
}
cuLaunchGridAsync(cuFunction, 100, 1, stream[i]
for (int i = 0; i < 2; ++i)
	cuMemcpyDtoHAsync(hostPtr + i * size, outputDevPtr + i * size, size, stream[i]);
cuEventRecord(stop, 0);
cuEventSynchronize(stop);
float elapsedTime;
cuEventElapsedTime(&elapsedTime, start, stop);

[텍스처 참조 관리]

관리를 위한 호출 기능texture reference

패스 kernel텍스처 메모리를 읽을 수 있으려면 먼저 텍스처에 바인딩되거나 텍스처 에서 사용되어야 합니다.texture referencetexture referencecuTexRefSetAddress()cuTexRefSetArray()

모듈에 다음과 같이 정의된 cuModule일부가 포함된 경우texture reference texReftexture<float, 2, cudaReadModeElementType> texRef;

다음 코드는 texRef에 대한 핸들을 얻는 방법을 보여줍니다.

CUtexref cuTexRef;
cuModuleGetTexRef(&cuTexRef, cuModule, “texRef”);

다음 코드는 가 가리키는 선형 메모리 texture reference에 a 를 바인딩하는 방법을 보여줍니다.devPtr

cuTexRefSetAddress(Null, cuTexRef, devPtr, size);

다음 코드는 a를 texture referenceCUDA 배열 cuArray에 바인딩하는 방법을 보여줍니다.

cuTexRefSetArray(cuTexRef, cuArrary, CU_TRSA_OVERRIDE_FORMAT);

texture reference주소 지정 모드, 필터 모드, 형식 및 기타 플래그를 설정합니다 .

텍스처를 바인딩하는 형식은 선언할 때 매개변수와 texture reference일치해야 합니다 .texture reference

그렇지 않으면 텍스처 가져오기 결과가 정의되지 않습니다.

[OpenGL 상호 운용성]

OpenGL상호 운용성을 제어하는 ​​함수 호출

OpenGL상호 운용성은 다음에 의해 cuGLInit()초기화 되어야 합니다.

버퍼 객체는CUDA

사용 cuGLRegisterBufferObject():

GLuint bufferObj;
cuGLRegisterBufferObject(bufferObj);

등록 후에는 kenrel을 통해 장치 메모리를 사용하여 버퍼 개체를 읽거나 쓸 수 있으며, 메모리 장치의 주소는 cuGLMapBufferObject()다음과 같이 반환됩니다.

GLuint bufferObj;
CUdeviceptr devPtr;
Int size;
cuGLMapBufferObject(&devPtr, &size, bufferObj);

지도를 제거하고 다음을 통해 등록하세요.cuGLUnmapBufferObject()和cuGLUnregisterBufferObject()

[Direct3D 상호 운용성]

Direct3D상호 운용성을 제어하는 ​​함수 호출

Direct3DcuD3D9Begin()상호 운용성은 다음에 의해 초기화되고 cuD3D9End()종료 되어야 합니다.

정점 객체 CUDAcuD3D9RegisterVertexBuffer()

LPDIRECT3DVERTEXBUFFER9 vertexBuffer;
cuD3D9RegisterVertexBuffer(vertexBuffer);

등록 후에는 kenrel을 통해 장치 메모리를 사용하여 버퍼 개체를 읽거나 쓸 수 있으며, 메모리 장치의 주소는 cuD3D9MapVertexBuffer()다음과 같이 반환됩니다.

LPDIRECT3DVERTEXBUFFER9 vertexBuffer;
CUdeviceptr devPtr;
Int size;
cuD3D9MapVertexBuffer(&devPtr, &size, vertexBuffer);

지도를 제거하고 다음을 통해 등록하세요.cuD3D9UnmapVertexBuffer()和cuD3D9UnregisterVertexBuffer()

추천

출처blog.csdn.net/qq_38973721/article/details/132392024