CUDA C编程手册: 编程接口(六)

CUDA C 运行时

表面内存

对于计算力高于2.0的设备,使用cudaArraySurfaceLoadStore标志创建的CUDA数组, 可以通过surface object.或者surface reference进行读写。不同的设备,所支持的表面内存的大小是不一样的。

表面对象API

表面对象的描述可以使用struct cudaResourceDesc来表示并通过调用cudaCreateSrufaceObject()来进行创建。
下示代码演示了对纹理进行简单变换的核函数的使用:

// Simple copy kernel
__global__ void copyKernel(cudaSurfaceObject_t inputSurfObj,
cudaSurfaceObject_t outputSurfObj,
int width, int height)
{
	// Calculate surface coordinates
	unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
	unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
	if (x < width && y < height) {
	uchar4 data;
	// Read from input surface
	surf2Dread(&data, inputSurfObj, x * 4, y);
	// Write to output surface
	surf2Dwrite(data, outputSurfObj, x * 4, y);
	}
}
// Host code
int main()
{
	// Allocate CUDA arrays in device memory
	cudaChannelFormatDesc channelDesc =
	cudaCreateChannelDesc(8, 8, 8, 8,
	cudaChannelFormatKindUnsigned);
	cudaArray* cuInputArray;
	cudaMallocArray(&cuInputArray, &channelDesc, width, height,
	cudaArraySurfaceLoadStore);
	cudaArray* cuOutputArray;
	cudaMallocArray(&cuOutputArray, &channelDesc, width, height,
	cudaArraySurfaceLoadStore);
	// Copy to device memory some data located at address h_data
	// in host memory
	cudaMemcpyToArray(cuInputArray, 0, 0, h_data, size,
	cudaMemcpyHostToDevice);
	// Specify surface
	struct cudaResourceDesc resDesc;
	memset(&resDesc, 0, sizeof(resDesc));
	resDesc.resType = cudaResourceTypeArray;
	// Create the surface objects
	resDesc.res.array.array = cuInputArray;
	cudaSurfaceObject_t inputSurfObj = 0;
	cudaCreateSurfaceObject(&inputSurfObj, &resDesc);
	resDesc.res.array.array = cuOutputArray;
	cudaSurfaceObject_t outputSurfObj = 0;
	cudaCreateSurfaceObject(&outputSurfObj, &resDesc);
	// Invoke kernel
	dim3 dimBlock(16, 16);
	dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x,
	(height + dimBlock.y - 1) / dimBlock.y);
	copyKernel<<<dimGrid, dimBlock>>>(inputSurfObj,
	outputSurfObj,
	width, height);
	// Destroy surface objects
	cudaDestroySurfaceObject(inputSurfObj);
	cudaDestroySurfaceObject(outputSurfObj);
	// Free device memory
	cudaFreeArray(cuInputArray);
	cudaFreeArray(cuOutputArray);
	return 0;
}

表面引用API

表面引用的声明的作用范围只是在文件 内:

surface<void, Type> surfRef;

其中,type指明了表面引用的类型,它可以是cudaSurgaceType1DcudaSurgaceType2DcudaSurgaceType3DcudaSurgaceTypeCubemapcudaSurgaceType1DLayeredcudaSurgaceType2DLayeredcudaSurgaceTypeCubemapLayered。该参数的默认值是cudaSurgaceType1D。一个表面纹理引用只能够被声明为全局静态变量,且不能够作为参数传递给函数。

在核函数使用表面引用来访存CUDA 数组前, 表面引用必须使用cudaBindSurfaceToArray()来与相应的数组进行绑定。

下面的代码演示了如何进行绑定:

// low-level API
surface<void, cudaSurfaceType2D> surfRef;
surfaceReference* surfRefPtr;
cudaGetSurfaceReference(&surfRefPtr, "surfRef");
cudaChannelFormatDesc channelDesc;
cudaGetChannelDesc(&channelDesc, cuArray);
cudaBindSurfaceToArray(surfRef, cuArray, &channelDesc);

// high-level API
surface<void, cudaSurfaceType2D> surfRef;
cudaBindSurfaceToArray(surfRef, cuArray);

一个CUDA 数组使用表面引用来进行读写时,维度和数据类型必须相匹配,否则是未定义的行为。不像纹理内存,表面内存使用的是字节寻址。也就是说,通过纹理函数来获取 x轴方向的纹理元的时后直接使用就可以了,而在表面函数需要乘上元素对应的字节大小。例如, 一维的浮点型CUDA 数组被绑定至纹理引用texRef时使用tex1d(texRef,x)来进行获取,而当绑定至surfRef时需要使用**surf1Dread(surfRef, 4*x)**来进行获取。

下示代码是对纹理使用了一些简单的变换。

// 2D surfaces
surface<void, 2> inputSurfRef;
surface<void, 2> outputSurfRef;

// Simple copy kernel
__global__ void copyKernel(int width, int height)
{
	// Calculate surface coordinates
	unsigned int x = blockIdx.x * blockDim.x + threadIdx.x;
	unsigned int y = blockIdx.y * blockDim.y + threadIdx.y;
	if (x < width && y < height) {
		uchar4 data;
		// Read from input surface
		surf2Dread(&data, inputSurfRef, x * 4, y);
		// Write to output surface
		surf2Dwrite(data, outputSurfRef, x * 4, y);
	}
}

// Host code
int main()
{
	// Allocate CUDA arrays in device memory
	cudaChannelFormatDesc channelDesc =
	cudaCreateChannelDesc(8, 8, 8, 8,
	cudaChannelFormatKindUnsigned);
	cudaArray* cuInputArray;
	cudaMallocArray(&cuInputArray, &channelDesc, width, height,
	cudaArraySurfaceLoadStore);
	cudaArray* cuOutputArray;
	cudaMallocArray(&cuOutputArray, &channelDesc, width, height,
	cudaArraySurfaceLoadStore);
	// Copy to device memory some data located at address h_data
	// in host memory
	cudaMemcpyToArray(cuInputArray, 0, 0, h_data, size,
	cudaMemcpyHostToDevice);
	// Bind the arrays to the surface references
	cudaBindSurfaceToArray(inputSurfRef, cuInputArray);
	cudaBindSurfaceToArray(outputSurfRef, cuOutputArray);
	// Invoke kernel
	dim3 dimBlock(16, 16);
	dim3 dimGrid((width + dimBlock.x - 1) / dimBlock.x,
	(height + dimBlock.y - 1) / dimBlock.y);
	copyKernel<<<dimGrid, dimBlock>>>(width, height);
	// Free device memory
	cudaFreeArray(cuInputArray);
	cudaFreeArray(cuOutputArray);
	return 0;
}

Cubemap 表面

cubemap表面的访存使用surfCubemapread()surfCubemapwrite()来实现。它通常被当做二维layered表面,使用一个整数索引来标记一个面,两个浮点数来寻址该面对应层内的纹理元。面的存储顺序可以通过查阅相关资料得知。

Cubemap Layered 表面

cubemap表面的访存使用surfCubemapLayeredread()surfCubemapLayeredwrite()来实现。它通常被当做二维layered表面,使用一个整数索引来标记一个cubemap中的一个面,两个浮点数来寻址该面对应层内的纹理元。面的存储顺序可以通过查阅相关资料得知。

CUDA 数组

CUDA 数组为了方便纹理的fetching, 其内存排布是经过了优化的,因此对外是不透明的。它可以是一维的、二维的、三维的或者是多种元素的组合。核函数对CUDA 数组的访存只能通过纹理的fetching或者表面的读写。

读写一致性

纹理和表面内存是被缓存的,在同一个核函数调用时, 缓存并不会与全局内存中保持一致。也就是说,一个线程只会在前一个核函数调用更新后后者内存拷贝之后才可以安全地读纹理或表面。如果是被核函数内的同一个线程或者其他线程更新再进行读取是不安全的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值