VS2017 CUDA编程学习10:纹理内存


VS2017 CUDA编程学习1:CUDA编程两变量加法运算
VS2017 CUDA编程学习2:在GPU上执行线程
VS2017 CUDA编程学习3:CUDA获取设备上属性信息
VS2017 CUDA编程学习4:CUDA并行处理初探 - 向量加法实现
VS2017 CUDA编程学习5:CUDA并行执行-线程
VS2017 CUDA编程学习6: GPU存储器架构
VS2017 CUDA编程学习7:线程同步-共享内存
VS2017 CUDA编程学习8:线程同步-原子操作
VS2017 CUDA编程学习9:常量内存


前言

这里继续CUDA编程的学习,今天学习了设备纹理内存的使用,这里分享给大家!


1. 纹理内存的理解

纹理内存,是设备中一种只读存储器,在设备内存中也有对应的缓冲器Cache来加速IO操作。纹理内存对于程序中有空间邻近性访问的数据很高效。这里的空间邻近性是指每个线程的读取位置和其他线程的读取位置邻近,这在图像处理中非常有用,因为图像处理中经常需要进行邻域比较,比如4邻域和8邻域。

当然全局内存也可以进行这种有空间邻近性特性数据的存储与访问,但是速度上要慢很多,需要进行大量的显存读取操作。但是纹理内存则只需要从显存读取一次就可以(不理解这种说法,书中这么说的),纹理内存支持2维和3维纹理读取操作。


2. C++ CUDA实现常量内存使用

这里以一个简单的例子:从纹理内存中读取数据并赋值。

纹理内存需要和“纹理引用”与CUDA数组配套使用来实现。
纹理引用通过 t e x t u r e < p a r a m 1 , p a r a m 2 , p a r a m 3 > texture<param1, param2, param3> texture<param1,param2,param3>类型变量来定义,param1表示纹理元素的类型,param2表示纹理引用的类型(1-表示1D, 2-表示2D, 3-表示3D),param3则表示读取模式,这是一个可选参数,说明是否要执行自动类型转换。

注意,纹理引用变量要确保被定义为全局静态变量(个人理解,这样才能在内核函数中使用),同时还要确保这个变量不能作为参数传递给任何其他函数。

下面的例子中通过纹理引用读取线程ID作为索引位置的数据,然后复制到d_out指针指向全局内存中。

在main函数中则使用CUDA数组 c u d a A r r a y cudaArray cudaArray并通过 c u d a M e m c p y T o A r r a y ( p a r a m 1 , p a r a m 2 , p a r a m 3 , p a r a m 4 , p a r a m 5 , p a r a m 6 ) cudaMemcpyToArray(param1, param2, param3, param4, param5, param6) cudaMemcpyToArray(param1,param2,param3,param4,param5,param6)对CUDA数组进行赋值,param1表示目标CUDA数组变量, param2和param3表示将主机数据赋值到CUDA数组横向和纵向的偏移量,(0, 0)表示CUDA数组的左上角开始赋值。

使用 c u d a B i n d T e x t u r e T o A r r a y ( ) cudaBindTextureToArray() cudaBindTextureToArray()函数将纹理引用和CUDA数组绑定,这样就可以通过访问纹理引用来获取纹理内存中存储的CUDA数组数据。这里使用texture相应的函数进行访问。当使用完纹理内存后,要执行解除纹理引用与CUDA数组的绑定的代码,即调用 c u d a U n b i n d T e x t u r e ( ) cudaUnbindTexture() cudaUnbindTexture()

详细代码如下所示:

#include <stdio.h>
#include <iostream>
#include <cuda.h>
#include <cuda_runtime.h>
#include <cuda_runtime_api.h>
#include <device_launch_parameters.h>

#ifndef __CUDACC__
#define __CUDACC__
#endif
#include <cuda_texture_types.h>//texture<> depend head file
#include <texture_fetch_functions.h>//tex1D() depend head file

#define NUM_THREADS 10
#define N 10

//定义1维的纹理引用
texture<float, 1, cudaReadModeElementType> textureRef;
//textureReference textureRef;

//定义内核函数:从纹理内存中获取数据并赋值给设备内存
__global__ void gpu_texture_memory(int n, float* d_out)
{
    
    
	int idx = blockIdx.x *blockDim.x + threadIdx.x;

	if (idx < n)
	{
    
    
		float temp = tex1D(textureRef, float(idx));
		d_out[idx] = temp;
	}
}

int main()
{
    
    
	int num_blocks = N / NUM_THREADS + ((N % NUM_THREADS) ? 1 : 0);
	float* d_out;
	cudaMalloc(&d_out, sizeof(float) * N);

	float* h_out = (float*)malloc(sizeof(float) * N);
	float h_in[N];
	for (int i = 0; i < N; i++)
	{
    
    
		h_in[i] = float(i);
	}

	//定义CUDA数组
	cudaArray *cu_Array;
	cudaMallocArray(&cu_Array, &textureRef.channelDesc, N, 1);
	cudaMemcpyToArray(cu_Array, 0, 0, h_in, sizeof(float)*N, cudaMemcpyHostToDevice);

	//绑定CUDA数组到纹理引用变量
	cudaBindTextureToArray(&textureRef, cu_Array, &textureRef.channelDesc);

	//调用内核函数
	gpu_texture_memory << <num_blocks, NUM_THREADS >> > (N, d_out);

	//拷贝结果到主机
	cudaMemcpy(h_out, d_out, sizeof(float)*N, cudaMemcpyDeviceToHost);
	printf("在GPU上使用纹理内存:\n");

	//打印结果
	for (int i = 0; i < N; i++)
	{
    
    
		printf("%f\n", h_out[i]);
	}

	//回收分配的资源
	free(h_out);
	cudaFree(d_out);
	cudaFreeArray(cu_Array);
	cudaUnbindTexture(&textureRef);

	system("pause");
	return 0;
}

3. 执行结果

在这里插入图片描述


总结

说实话,这里还是无法体会到纹理内存的提速,这里只是简单介绍了纹理内存的用法,由于这里只是记录个人的学习笔记,所以如果有误,还请原谅,如果能够指正,万分感谢!

学习资料

《基于GPU加速的计算机视觉编程》

Guess you like

Origin blog.csdn.net/DU_YULIN/article/details/121109585