CUDA by example Chapter7 纹理内存

VS2017 CUDA编程学习10:纹理内存 文章目录前言1. 纹理内存的理解2. C++ CUDA实现常量内存使用3. 执行结果总结学习资料 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:线程同步-共享内 阅读详情

纹理内存简介

和常量内存一样,纹理内存是另一种类型的只读内存。纹理内存是专门为那些在内存访问模式中存在大量空间局部性的图形应用程序而设计的。假如在某个计算应用程序中,一个线程读取的位置与邻近线程读取的位置"非常接近",使用纹理内存将会减少内存通信量,从而提高性能。
在这里插入图片描述

热传导模拟

简单的传热模型

假设一个矩形网格,热源分布如下。假设热源单元本身的温度保持不变,热量可以在相邻单元之间流动,从高温单元传导到低温单元。
在这里插入图片描述
温度更新的计算方法:
将单元与其邻接单元的温差加起来,然后加上原有温度,等于新时刻的温度:
TNEW = TOLD+sum_NEIGHBOR(k(TNEIGHBOR-TOLD))
由于邻接单元只有上下左右四个,将上式化简,得到:
TNEW=TOLD+k(TTOP+TBOTTOm+TLEFT+TRIGHT-4TOLD)

代码中温度更新的计算

更新流程:

  • 给定包含初始温度的网格,它的大部分单元都是0,少部分是初始温度值。将其中作为热源的初始温度值复制到当前时间的网格对应单元中。确保"加热单元将保持恒温",这个复制操作时在copy_const_kernal()中执行的。
  • 给定一个输入温度网格,根据温度更新的公式计算输出温度网格。这个更新操作是在blend_kernal()中执行的。
  • 将输入温度网格和输出温度网格交换,为下一个步骤的计算做好准备。当模拟下一个时间步时,步骤2中计算得到的输出温度网格将成为步骤1中的输入温度网格。

GPU不使用纹理内存的热传导模拟计算

完整代码

// 代码7.3不使用纹理内存的热传导模型计算
//时间:2019.07.28
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_anim.h"
#include "book.h"

#define DIM 1024
#define SPEED 0.25f
#define PI 3.1415926535897932f
#define MAX_TEMP 1.0f
#define MIN_TEMP 0.0001f



//更新函数中需要的全局变量
struct DataBlock
{
	unsigned char *output_bitmap;
	float *dev_inSrc;
	float *dev_outSrc;
	float *dev_constSrc;
	CPUAnimBitmap *bitmap;

	cudaEvent_t start, stop;
	float totalTime;
	float frames;
};

__global__ void copy_const_kernal(float *iptr, const float *cptr)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	if (cptr[offset] != 0)iptr[offset] = cptr[offset];

}

__global__ void blend_kernal(float *outSrc, const float *inSrc)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	int left = offset - 1;
	int right = offset + 1;
	if (x == 0)left++;
	if (x == DIM - 1)right--;

	int top = offset - DIM;
	int bottom = offset + DIM;
	if (y == 0)top += DIM;
	if (y == DIM - 1)bottom -= DIM;

	outSrc[offset] = inSrc[offset] + SPEED*(inSrc[top] + inSrc[bottom] + inSrc[left] + inSrc[right] - inSrc[offset] * 4);

}


//在swap这里栽了两天的跟头
void swap(float **a, float **b)
{
	float *c;
	c = *a;
	*a = *b;
	*b = c;
}



void anim_gpu(DataBlock *d, int ticks)
{
	cudaEventRecord(d->start, 0);
	dim3 grids(DIM / 16, DIM / 16);
	dim3 blocks(16, 16);
	CPUAnimBitmap *bitmap = d->bitmap;
	float *c;
	for (int i = 0; i < 90; i++)
	{
		//printf("faf");
		copy_const_kernal << <grids, blocks >> > ( d->dev_inSrc, d->dev_constSrc);
		blend_kernal << <grids, blocks >> > ( d->dev_outSrc, d->dev_inSrc );

		swap(&d->dev_inSrc,&d->dev_outSrc);
	}
	//将float数值映射成颜色,以便用图像显示它
	float_to_color << <grids, blocks >> >(d->output_bitmap,d->dev_inSrc);
	cudaMemcpy(bitmap->get_ptr(), d->output_bitmap, bitmap->image_size(), cudaMemcpyDeviceToHost);
	cudaEventRecord(d->stop,0);
	cudaEventSynchronize(d->stop);
	float elapsedTime;
	cudaEventElapsedTime(&elapsedTime, d->start, d->stop);
	d->totalTime += elapsedTime;
	++d->frames;
	printf("Average Time per frame:%3.1fms\n", d->totalTime / d->frames);
}

void anim_exit(DataBlock *d)
{
	cudaFree(d->dev_constSrc);
	cudaFree(d->dev_inSrc);
	cudaFree(d->dev_outSrc);

	cudaEventDestroy(d->start);
	cudaEventDestroy(d->stop);
}

int main()
{
	DataBlock data;
	CPUAnimBitmap bitmap(DIM, DIM, &data);
	data.bitmap = &bitmap;
	data.totalTime = 0;
	data.frames = 0;
	cudaEventCreate(&data.start);
	cudaEventCreate(&data.stop);
	cudaMalloc((void **)&data.output_bitmap, bitmap.image_size());
	cudaMalloc((void**)&data.dev_constSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_inSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_outSrc, bitmap.image_size());

	//在CPU上分配临时内存,对其填充一些内容,用于初始化data.dev_constSrc和data.dev_inSrc
	float *temp = (float *)malloc(bitmap.image_size());
	for (int i = 0; i < DIM*DIM; i++)
	{
		temp[i] = 0;
		int x = i % DIM;
		int y = i / DIM;
		if ((x>300) && (x<600) && (y>310) && (y < 601))
		{
			temp[i] = MAX_TEMP;
		}
	}
	temp[DIM * 100 + 100] = (MAX_TEMP + MIN_TEMP) / 2;
	temp[DIM * 700 + 100] = MIN_TEMP;
	temp[DIM * 300 + 300] = MIN_TEMP;
	temp[DIM * 200 + 700] = MIN_TEMP;
	for (int y = 800; y < 900; y++)
	{
		for (int x = 400; x < 500; x++)
		{
			temp[x + y*DIM] = MIN_TEMP;
		}
	}
	//用当前填充的内容初始化data.dev_constSrc
	cudaMemcpy(data.dev_constSrc, temp, bitmap.image_size(), cudaMemcpyHostToDevice);

	//继续向temp中填充一些内容
	for (int y = 800; y < DIM; y++)
	{
		for (int x = 0; x < 200; x++)
		{
			temp[x + y*DIM] = MAX_TEMP;
		}
	}
	//用给当前填充的内容初始化data.dev_inSrc
	cudaMemcpy(data.dev_inSrc, temp, bitmap.image_size(), cudaMemcpyHostToDevice);

	//释放CPU端的临时内存
	free(temp);
	//用anim_gpu生成动画的每一帧,用anim_exit退出动画显示并释放内存
	bitmap.anim_and_exit((void(*) (void *, int))anim_gpu, (void(*)(void *))anim_exit);
}

运行结果
在这里插入图片描述
在这里插入图片描述
需要极其注意,更新步骤中的第三步,交换输入网格和输出网格:
因为dev_inSrc和dev_outSrc分别是两幅图像的入口指针,将二者交换只需要:

float *swap;
swap = dev_inSrc;
dev_inSrc = dev_outSrc;
dev_outSrc = swap;

但是,如果使用函数swap来完成这个工作,由于函数中传入的是形参,形参的改变并不会改变传入参数的原始值,因此我们需要将&dev_inSrc和&dev_outSrc传入swap函数中,才能完成交换任务

//在swap这里栽了两天的跟头
void swap(float **a, float **b)
{
	float *c;
	c = *a;
	*a = *b;
	*b = c;
}

//使用方法
swap(&d->dev_inSrc,&d->dev_outSrc);

GPU使用一维纹理内存的热传导模拟计算

1. 纹理内存的使用场景
在温度更新计算的内存访问模式中存在着巨大的内存空间局部性,这种访问模式可以通过GPU纹理内存来加速。
2.一维纹理内存变量引用的声明

//声明纹理内存变量的引用,这些变量将位于GPU上
texture<float> texConstSrc;
texture<float> texIn;
texture<float> texOut;

3.一维纹理内存引用变量与实际内存缓冲区的绑定
通过cudaBindTexture()可以将纹理内存引用变量与内存缓冲区进行绑定。

	cudaMalloc((void **)&data.output_bitmap, bitmap.image_size());
	cudaMalloc((void**)&data.dev_constSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_inSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_outSrc, bitmap.image_size());

	//分配内存之后,需要将这些变量绑定到之前声明的纹理引用上去
	cudaBindTexture(NULL, texConstSrc, data.dev_constSrc, bitmap.image_size());
	cudaBindTexture(NULL, texIn, data.dev_inSrc,  bitmap.image_size());
	cudaBindTexture(NULL, texOut, data.dev_outSrc,  bitmap.image_size());

4.读取一维纹理内存方法
使用tex1Dfetch()读取一维纹理内存,传给blend_kernal的dstOut是一个bool变量,用于指明读取哪一个缓冲区作为输入。

__global__ void blend_kernal(float *dst, bool dstOut)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	int left = offset - 1;
	int right = offset + 1;
	if (x == 0)left++;
	if (x == DIM - 1)right--;

	int top = offset - DIM;
	int bottom = offset + DIM;
	if (y == 0)top += DIM;
	if (y == DIM - 1)bottom -= DIM;

	float t, b, l, r, c;
	if (dstOut)
	{
		t = tex1Dfetch(texIn,top);
		b = tex1Dfetch(texIn, bottom);
		l = tex1Dfetch(texIn, left);
		r = tex1Dfetch(texIn, right);
		c = tex1Dfetch(texIn, offset);
	}
	else
	{
		t = tex1Dfetch(texOut, top);
		b = tex1Dfetch(texOut, bottom);
		l = tex1Dfetch(texOut, left);
		r = tex1Dfetch(texOut, right);
		c = tex1Dfetch(texOut, offset);
	}

	dst[offset] = c + SPEED*(t + b + l + r - 4 * c);
}

5.完整代码

// 代码7.3.4使用一维纹理内存的热传导模型计算
//时间:2019.07.29
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_anim.h"
#include "cuda.h"
#include "book.h"

#define DIM 1024
#define SPEED 0.25f
#define PI 3.1415926535897932f
#define MAX_TEMP 1.0f
#define MIN_TEMP 0.0001f

//声明纹理内存变量的引用,这些变量将位于GPU上
texture<float> texConstSrc;
texture<float> texIn;
texture<float> texOut;




//更新函数中需要的全局变量
struct DataBlock
{
	unsigned char *output_bitmap;
	float *dev_inSrc;
	float *dev_outSrc;
	float *dev_constSrc;
	CPUAnimBitmap *bitmap;

	cudaEvent_t start, stop;
	float totalTime;
	float frames;
};

__global__ void copy_const_kernal(float *iptr)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	float c = tex1Dfetch(texConstSrc, offset);
	if (c != 0)iptr[offset] = c;

}

__global__ void blend_kernal(float *dst, bool dstOut)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	int left = offset - 1;
	int right = offset + 1;
	if (x == 0)left++;
	if (x == DIM - 1)right--;

	int top = offset - DIM;
	int bottom = offset + DIM;
	if (y == 0)top += DIM;
	if (y == DIM - 1)bottom -= DIM;

	float t, b, l, r, c;
	if (dstOut)
	{
		t = tex1Dfetch(texIn,top);
		b = tex1Dfetch(texIn, bottom);
		l = tex1Dfetch(texIn, left);
		r = tex1Dfetch(texIn, right);
		c = tex1Dfetch(texIn, offset);
	}
	else
	{
		t = tex1Dfetch(texOut, top);
		b = tex1Dfetch(texOut, bottom);
		l = tex1Dfetch(texOut, left);
		r = tex1Dfetch(texOut, right);
		c = tex1Dfetch(texOut, offset);
	}

	dst[offset] = c + SPEED*(t + b + l + r - 4 * c);
}





void anim_gpu(DataBlock *d, int ticks)
{
	cudaEventRecord(d->start, 0);
	dim3 grids(DIM / 16, DIM / 16);
	dim3 blocks(16, 16);
	CPUAnimBitmap *bitmap = d->bitmap;

	//由于tex是全局并且是有界的,因此我们必须通过一个标志来来选择每次迭代中哪个是输入/输出
	volatile bool dstOut = true;
	for (int i = 0; i < 90; i++)
	{
		float *in, *out;
		if (dstOut)
		{
			in = d->dev_inSrc;
			out = d->dev_outSrc;
		}
		else
		{
			in = d->dev_outSrc;
			out = d->dev_inSrc;
		}
		copy_const_kernal << <grids, blocks >> > (in);
		blend_kernal << <grids, blocks >> > (out,dstOut);
		dstOut = !dstOut;
	}
	//将float数值映射成颜色,以便用图像显示它
	float_to_color << <grids, blocks >> >(d->output_bitmap, d->dev_inSrc);
	cudaMemcpy(bitmap->get_ptr(), d->output_bitmap, bitmap->image_size(), cudaMemcpyDeviceToHost);
	cudaEventRecord(d->stop, 0);
	cudaEventSynchronize(d->stop);
	float elapsedTime;
	cudaEventElapsedTime(&elapsedTime, d->start, d->stop);
	d->totalTime += elapsedTime;
	++d->frames;
	printf("Average Time per frame:%3.1fms\n", d->totalTime / d->frames);
}

void anim_exit(DataBlock *d)
{
	cudaUnbindTexture(texIn);
	cudaUnbindTexture(texOut);
	cudaUnbindTexture(texConstSrc);

	cudaFree(d->dev_constSrc);
	cudaFree(d->dev_inSrc);
	cudaFree(d->dev_outSrc);

	cudaEventDestroy(d->start);
	cudaEventDestroy(d->stop);
}

int main()
{
	DataBlock data;
	CPUAnimBitmap bitmap(DIM, DIM, &data);
	data.bitmap = &bitmap;
	data.totalTime = 0;
	data.frames = 0;
	cudaEventCreate(&data.start);
	cudaEventCreate(&data.stop);
	cudaMalloc((void **)&data.output_bitmap, bitmap.image_size());
	cudaMalloc((void**)&data.dev_constSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_inSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_outSrc, bitmap.image_size());

	//分配内存之后,需要将这些变量绑定到之前声明的纹理引用上去
	//cudaChannelFormatDesc desc = cudaCreateChannelDesc<float>();
	cudaBindTexture(NULL, texConstSrc, data.dev_constSrc, bitmap.image_size());
	cudaBindTexture(NULL, texIn, data.dev_inSrc,  bitmap.image_size());
	cudaBindTexture(NULL, texOut, data.dev_outSrc,  bitmap.image_size());


	//在CPU上分配临时内存,对其填充一些内容,用于初始化data.dev_constSrc和data.dev_inSrc
	float *temp = (float *)malloc(bitmap.image_size());
	for (int i = 0; i < DIM*DIM; i++)
	{
		temp[i] = 0;
		int x = i % DIM;
		int y = i / DIM;
		if ((x>300) && (x<600) && (y>310) && (y < 601))
		{
			temp[i] = MAX_TEMP;
		}
	}
	temp[DIM * 100 + 100] = (MAX_TEMP + MIN_TEMP) / 2;
	temp[DIM * 700 + 100] = MIN_TEMP;
	temp[DIM * 300 + 300] = MIN_TEMP;
	temp[DIM * 200 + 700] = MIN_TEMP;
	for (int y = 800; y < 900; y++)
	{
		for (int x = 400; x < 500; x++)
		{
			temp[x + y*DIM] = MIN_TEMP;
		}
	}
	//用当前填充的内容初始化data.dev_constSrc
	cudaMemcpy(data.dev_constSrc, temp, bitmap.image_size(), cudaMemcpyHostToDevice);

	//继续向temp中填充一些内容
	for (int y = 800; y < DIM; y++)
	{
		for (int x = 0; x < 200; x++)
		{
			temp[x + y*DIM] = MAX_TEMP;
		}
	}
	//用给当前填充的内容初始化data.dev_inSrc
	cudaMemcpy(data.dev_inSrc, temp, bitmap.image_size(), cudaMemcpyHostToDevice);

	//释放CPU端的临时内存
	free(temp);
	//用anim_gpu生成动画的每一帧,用anim_exit退出动画显示并释放内存
	bitmap.anim_and_exit((void(*) (void *, int))anim_gpu, (void(*)(void *))anim_exit);
}

6.运行结果
在这里插入图片描述
在这里插入图片描述
bang!!!性能并没有得到提升,反而急剧恶化,不知所措,如果有哪位同学遇到跟我同样的问题,欢迎联系我讨论

GPU使用二维纹理内存的热传导模拟计算

1. 二维纹理内存变量引用的声明

//声明纹理内存变量的引用,这些变量将位于GPU上
texture<float,2> texConstSrc;
texture<float,2> texIn;
texture<float,2> texOut;

2.二维纹理内存引用变量与实际内存缓冲区的绑定
通过cudaBindTexture2D()可以将纹理内存引用变量与内存缓冲区进行绑定。
绑定二维纹理时,CUDA运行时要求提供一个cudaChannelFormatDesc,保持默认设置就可以了。

	cudaMalloc((void **)&data.output_bitmap, bitmap.image_size());
	cudaMalloc((void**)&data.dev_constSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_inSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_outSrc, bitmap.image_size());

	//分配内存之后,需要将这些变量绑定到之前声明的纹理引用上去
	cudaChannelFormatDesc desc = cudaCreateChannelDesc<float>();
	cudaBindTexture2D(NULL, texConstSrc, data.dev_constSrc, desc, DIM, DIM, sizeof(float)*DIM);
	cudaBindTexture2D(NULL, texIn, data.dev_inSrc, desc, DIM, DIM, sizeof(float)*DIM);
	cudaBindTexture2D(NULL, texOut, data.dev_outSrc, desc, DIM, DIM, sizeof(float)*DIM);

3.读取二维纹理内存方法
通过tex2D()读取二维纹理内存

__global__ void blend_kernal(float *dst, bool dstOut)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	int left = offset - 1;
	int right = offset + 1;
	if (x == 0)left++;
	if (x == DIM - 1)right--;

	int top = offset - DIM;
	int bottom = offset + DIM;
	if (y == 0)top += DIM;
	if (y == DIM - 1)bottom -= DIM;

	float t, b, l, r, c;
	if (dstOut)
	{
		t = tex2D(texIn, x, y - 1);
		l = tex2D(texIn, x - 1, y);
		c = tex2D(texIn, x, y);
		r = tex2D(texIn, x + 1, y);
		b = tex2D(texIn, x, y + 1);
	}
	else
	{
		t = tex2D(texOut, x, y - 1);
		l = tex2D(texOut, x - 1, y);
		c = tex2D(texOut, x, y);
		r = tex2D(texOut, x + 1, y);
		b = tex2D(texOut, x, y + 1);
	}

	dst[offset] = c + SPEED*(t + b + l + r - 4 * c);
}

4.完整代码

// 代码7.3.4使用二维纹理内存的热传导模型计算
//时间:2019.07.29
#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <iostream>
#include "cpu_anim.h"
#include "cuda.h"
#include "book.h"

#define DIM 1024
#define SPEED 0.25f
#define PI 3.1415926535897932f
#define MAX_TEMP 1.0f
#define MIN_TEMP 0.0001f

//声明纹理内存变量的引用,这些变量将位于GPU上
texture<float,2> texConstSrc;
texture<float,2> texIn;
texture<float,2> texOut;




//更新函数中需要的全局变量
struct DataBlock
{
	unsigned char *output_bitmap;
	float *dev_inSrc;
	float *dev_outSrc;
	float *dev_constSrc;
	CPUAnimBitmap *bitmap;

	cudaEvent_t start, stop;
	float totalTime;
	float frames;
};

__global__ void copy_const_kernal(float *iptr)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	float c = tex2D(texConstSrc, x, y);
	if (c != 0)iptr[offset] = c;

}

__global__ void blend_kernal(float *dst, bool dstOut)
{
	//将threadIdx/blockIdx映射到像素位置
	int x = threadIdx.x + blockIdx.x*blockDim.x;
	int y = threadIdx.y + blockIdx.y*blockDim.y;
	int offset = x + y*blockDim.x*gridDim.x;

	int left = offset - 1;
	int right = offset + 1;
	if (x == 0)left++;
	if (x == DIM - 1)right--;

	int top = offset - DIM;
	int bottom = offset + DIM;
	if (y == 0)top += DIM;
	if (y == DIM - 1)bottom -= DIM;

	float t, b, l, r, c;
	if (dstOut)
	{
		t = tex2D(texIn, x, y - 1);
		l = tex2D(texIn, x - 1, y);
		c = tex2D(texIn, x, y);
		r = tex2D(texIn, x + 1, y);
		b = tex2D(texIn, x, y + 1);
	}
	else
	{
		t = tex2D(texOut, x, y - 1);
		l = tex2D(texOut, x - 1, y);
		c = tex2D(texOut, x, y);
		r = tex2D(texOut, x + 1, y);
		b = tex2D(texOut, x, y + 1);
	}

	dst[offset] = c + SPEED*(t + b + l + r - 4 * c);
}





void anim_gpu(DataBlock *d, int ticks)
{
	cudaEventRecord(d->start, 0);
	dim3 grids(DIM / 16, DIM / 16);
	dim3 blocks(16, 16);
	CPUAnimBitmap *bitmap = d->bitmap;

	//由于tex是全局并且是有界的,因此我们必须通过一个标志来来选择每次迭代中哪个是输入/输出
	volatile bool dstOut = true;
	for (int i = 0; i < 90; i++)
	{
		float *in, *out;
		if (dstOut)
		{
			in = d->dev_inSrc;
			out = d->dev_outSrc;
		}
		else
		{
			in = d->dev_outSrc;
			out = d->dev_inSrc;
		}
		copy_const_kernal << <grids, blocks >> > (in);
		blend_kernal << <grids, blocks >> > (out, dstOut);
		dstOut = !dstOut;
	}
	//将float数值映射成颜色,以便用图像显示它
	float_to_color << <grids, blocks >> >(d->output_bitmap, d->dev_inSrc);
	cudaMemcpy(bitmap->get_ptr(), d->output_bitmap, bitmap->image_size(), cudaMemcpyDeviceToHost);
	cudaEventRecord(d->stop, 0);
	cudaEventSynchronize(d->stop);
	float elapsedTime;
	cudaEventElapsedTime(&elapsedTime, d->start, d->stop);
	d->totalTime += elapsedTime;
	++d->frames;
	printf("Average Time per frame:%3.1fms\n", d->totalTime / d->frames);
}

void anim_exit(DataBlock *d)
{
	cudaUnbindTexture(texIn);
	cudaUnbindTexture(texOut);
	cudaUnbindTexture(texConstSrc);

	cudaFree(d->dev_constSrc);
	cudaFree(d->dev_inSrc);
	cudaFree(d->dev_outSrc);

	cudaEventDestroy(d->start);
	cudaEventDestroy(d->stop);
}

int main()
{
	DataBlock data;
	CPUAnimBitmap bitmap(DIM, DIM, &data);
	data.bitmap = &bitmap;
	data.totalTime = 0;
	data.frames = 0;
	cudaEventCreate(&data.start);
	cudaEventCreate(&data.stop);
	cudaMalloc((void **)&data.output_bitmap, bitmap.image_size());
	cudaMalloc((void**)&data.dev_constSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_inSrc, bitmap.image_size());
	cudaMalloc((void **)&data.dev_outSrc, bitmap.image_size());

	//分配内存之后,需要将这些变量绑定到之前声明的纹理引用上去
	cudaChannelFormatDesc desc = cudaCreateChannelDesc<float>();
	cudaBindTexture2D(NULL, texConstSrc, data.dev_constSrc, desc, DIM, DIM, sizeof(float)*DIM);
	cudaBindTexture2D(NULL, texIn, data.dev_inSrc, desc, DIM, DIM, sizeof(float)*DIM);
	cudaBindTexture2D(NULL, texOut, data.dev_outSrc, desc, DIM, DIM, sizeof(float)*DIM);


	//在CPU上分配临时内存,对其填充一些内容,用于初始化data.dev_constSrc和data.dev_inSrc
	float *temp = (float *)malloc(bitmap.image_size());
	for (int i = 0; i < DIM*DIM; i++)
	{
		temp[i] = 0;
		int x = i % DIM;
		int y = i / DIM;
		if ((x>300) && (x<600) && (y>310) && (y < 601))
		{
			temp[i] = MAX_TEMP;
		}
	}
	temp[DIM * 100 + 100] = (MAX_TEMP + MIN_TEMP) / 2;
	temp[DIM * 700 + 100] = MIN_TEMP;
	temp[DIM * 300 + 300] = MIN_TEMP;
	temp[DIM * 200 + 700] = MIN_TEMP;
	for (int y = 800; y < 900; y++)
	{
		for (int x = 400; x < 500; x++)
		{
			temp[x + y*DIM] = MIN_TEMP;
		}
	}
	//用当前填充的内容初始化data.dev_constSrc
	cudaMemcpy(data.dev_constSrc, temp, bitmap.image_size(), cudaMemcpyHostToDevice);

	//继续向temp中填充一些内容
	for (int y = 800; y < DIM; y++)
	{
		for (int x = 0; x < 200; x++)
		{
			temp[x + y*DIM] = MAX_TEMP;
		}
	}
	//用给当前填充的内容初始化data.dev_inSrc
	cudaMemcpy(data.dev_inSrc, temp, bitmap.image_size(), cudaMemcpyHostToDevice);

	//释放CPU端的临时内存
	free(temp);
	//用anim_gpu生成动画的每一帧,用anim_exit退出动画显示并释放内存
	bitmap.anim_and_exit((void(*) (void *, int))anim_gpu, (void(*)(void *))anim_exit);
}

5.运行结果
在这里插入图片描述
在这里插入图片描述
bang!!!性能并没有得到提升,反而急剧恶化,不知所措,如果有哪位同学遇到跟我同样的问题,欢迎联系我讨论

CUDA:纹理内存的使用实例 它还指定了纹理类型为cudaTextureType1D,表示我们正在处理的数据是一维的。这个模式表示,当我们通过纹理采样器读取纹理时,返回的是单个元素值,而不是一个颜色向量。这里,我们将数据“data”绑定到了我们之前声明的texture对象“texRef”上。在这个示例中,我们生成了一个256元的正弦波,并将其绑定到纹理内存中。通过使用纹理内存,我们可以以高效的方式访问只读数据,并利用硬件加速器来提高应用程序的性能。一旦我们将数据绑定到了texture对象上,我们就可以在CUDA内核函数中使用它了。 阅读详情

相关推荐

从零实现地震波场模拟:交错网格有限差分法核心代码精讲

本文详细介绍了从零实现地震波场模拟的核心技术,重点讲解了交错网格有限差分法的实现过程。通过速度-应力弹性波方程的离散化处理,结合代码实例演示了应力与速度更新的关键步骤,并分享了震源加载、稳定性条件等实用技巧,为计算地球物理学研究提供实践指导。

weixin_42556197的博客 410

[菜鸟每天来段CUDA_C]CUDA实现简单热传导动态模拟

本文利用CUDA实现简单二维平面上的热传导模拟。假设有一个矩形房间,将其分成一个网格,在网格中随机散布一些热源,热源有不同的固定温度,然后就可以计算网格中每个单元的温度随时间的变化情况。本文将热传导模型做如下简化:某单元的新时刻温度等于原有温度加其与邻接单元的温差,邻接单元取上下左右四个单元。

Running Jonny 3641

CMU开源NavRL:深度强化学习如何破解无人机动态避障难题?

卡内基梅隆大学(CMU)开源了NavRL框架,利用深度强化学习解决无人机在复杂环境中的动态避障难题。该框架通过创新的状态表示、多目标奖励函数设计以及独立的安全屏障机制,使无人机能够通过仿真训练自主学会灵活、安全的导航策略,并成功实现从仿真到真实环境的零样本迁移。

hope8的博客 721

CUDA By Example(六)——纹理内存

CUDA C中纹理内存使用的说明,并以热传导模拟为例

hhhcbw的博客,欢迎各位来访 1568

利用WebDAV协议将百度网盘挂载为本地网络硬盘的完整指南

WebDAV是一种基于HTTP/HTTPS的扩展协议,它允许客户端通过标准HTTP方法对远程服务器上的文件进行读写和管理。其核心原理是通过定义如PROPFIND、PUT、GET等标准化方法,实现跨平台的远程文件系统访问。这项技术的价值在于打破了不同存储服务间的壁垒,将封闭的云存储生态接入开放标准协议体系。在工程实践中,WebDAV常被用于实现跨设备文件同步、媒体服务器集成和生产力工具数据备份等场景。通过搭建WebDAV服务器作为协议转换层,可以将百度网盘这类封闭存储服务映射为标准网络存储,实现类似bdpan

weixin_42523907的博客 2441

开源AI数字人DUIX:从零构建你的智能交互助手

本文详细介绍了如何利用开源AI数字人DUIX从零构建智能交互助手。文章阐述了DUIX作为开源项目的核心优势,包括低成本启动、低网络依赖和高度可定制性,并提供了从开发环境搭建、核心模块(AI模型、语音、形象)配置到个性化业务逻辑开发与最终部署的完整实践指南,帮助开发者快速掌握构建专属AI数字人的全流程。

weixin_29051811的博客 505

简单的传热模型的动画模拟

/* 简单的二维热传导模拟: 首先,假设有一个矩形房间,将其分成一个网格,在网格中随机散布一些热源, 他们有着不同的固定温度,在给定了矩形网格及热源分布后可以计算网格中每 个单元的温度随时间的变化情况,为了简单热源单元本身的温度将保持不变, 在时间递进的每个步骤中,假设热量在某个单干及其邻接单元之间流动,如果 某个单元的临接单元的温度更高,那么热量将从临接单元传导到该单元,相反的 如

wufenxia的专栏 4375

VSCode CMake 安装与配置详解

概述 在windows上使用cmake编译C/C++程序时,首先需要CMake,安装gcc/g++编译环境,然后使用VSCode 以及配置下CMakelist.txt。 所需工具: VSCode (需要安装以下插件) C/C++ C++ Intellisense CMake CMake tools CMake Tools Helper CMake MinGW 安装CMake 下载链接:...

Markus 8万+

使用用一维纹理存储器的热传导模拟

#include #include "book.h" #include "cpu_anim.h" #define DIM 1024 #define PI 3.141592653f #define MAX_TEMP 1.0f #define MIN_TEMP 0.0001f #define SPEED 0.25f /*在温度更新计算的内存访问模式中存在着巨大的内存空间局部性,这种访

wufenxia的专栏 2115

软件可靠性设计

本文系统介绍了软件可靠性设计与建模的理论框架和实施方法。首先从可靠性定义、核心思想和与建模的关系等理论基础出发,重点阐述了模块化设计、冗余容错、异常处理等六类可靠性设计方法及其技术栈。文章详细分析了可靠性设计在软件生命周期各阶段的应用,并深入探讨了设计与建模的深度结合机制,包括参数影响、验证方法和量化指标。最后提出了覆盖设计、开发、测试和运行四个阶段的落地策略,形成完整的"设计-建模-运行"流程体系。全文强调通过预防性设计和量化建模相结合的方法提升软件系统的可靠性和稳定性。

moton2017的博客 896

ABAP CDS SELECT, parameters 全面解析:把参数化视图玩明白(含可运行示例)

这篇技术博客介绍了ABAP CDS中参数化视图的用法与实践。文章从业务需求出发,解释了参数化视图的必要性,并详细说明了语法规则、参数类型匹配、视图内部参数引用等关键点。重点展示了在JOIN和ASSOCIATION中传递参数的写法,提供了航线分析的完整业务案例和可运行代码示例。文章还总结了视图实体与DDIC视图的差异、会话变量使用技巧等实践要点,特别强调了JOIN中必须显式传参的语法要求。通过真实场景演示,帮助开发者掌握参数化CDS的高效应用。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 364

《CUDA By Example》【Chapter 07】纹理内存 ?

7.1 本章目标 纹理内存和常量内存一样,都是只读内存,用来减少带宽流量,提升性能。 1,了解纹理内存的特性 2,了解如何在CUDA C中使用一维纹理内存 3,了解如何在CUDA C中使用二维纹理内存 7.2 纹理内存简介(Texture Memory) 7.3 热传导模拟...

wondervictor的博客 357

【ComfyUI】FluxKontext 身形调优与体态增强

AI身形调优与体态增强解决方案 该工作流通过Flux系列模型实现人物体态的智能化增强,主要特点包括: 采用UNet主体模型+双文本编码器+身形增强LoRA的架构组合 通过ReferenceLatent保持原始姿态的同时强化胸腰臀比例 完整工作流包含图像加载、文本编码、潜空间映射等8个核心节点 支持自动化的前后效果对比展示 应用优势: 保持人物自然姿态的前提下实现局部强化 支持通过Prompt精确控制增强方向 适用于摄影后期、角色美化等创作场景 输出效果稳定且风格统一 使用方

Mr数据杨 1303

Hands-On GPU加速计算机视觉全解析:OpenCV+CUDA从入门到实战的完整路线图

如果你正在寻找一条系统学习 **GPU加速计算机视觉** 的完整路径,那么 Hands-On-GPU-Accelerated-Computer-Vision-with-OpenCV-and-CUDA 这个开源项目就是为你准备的宝藏仓库。它源自 Packt 出版的同名经典书籍,由资深计算机视觉工程师 Bhaumik Vaidya 编写,将 **OpenCV + CUDA** 双技术栈的实战代码全部开

gitblog_01053的博客 759

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

基于纹理内存的CUDA热传导模拟

原文链接 项目中有三个,第一个是全局内存,其余两个分别是基于1d和2d纹理内存。项目打包下载。 纹理内存是只读内存,与常量内存相同的是,纹理内存也缓存在芯片中,因此某些情况下,它能减少对内存的请求并提供更高效的内存宽带。纹理内存专门为那些内存访问模式中存在大量空间局部性的图形应用程序而设计的。在某个计算应用程序中,这意味着一个线程读取的位置可能与邻近线程读取的位置“非常接近”。纹理缓存为了加速...

weixin_30677475的博客 247

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

CUDA Samples: Long Vector Add

CUDA Samples: Long Vector Add

网络资源是无限的 1304

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

CUDA一维纹理内存

纹理一词来源于GPU图形世界,GPU通用并行计算“盗用”了纹理一词,定义了一个纹理内存的概念。纹理内存缓存在 设备上,在某些情况下能减少对内存的请求并降低内存带宽的使用,是专门为那些在内存访问模式中存在大量空间局部性的图形应用而设计,意味着一个线程读取的位置可能与邻近线程读取的位置“非常接近”。对于GPU内核而言,纹理内存是只读内存,并且只有通过特殊的纹理API...

weixin_30467087的博客 304

使用二维纹理内存模拟热传导

#include #include "book.h" #include "cpu_anim.h" #define DIM 1024 #define PI 3.141592653f #define MAX_TEMP 1.0f #define MIN_TEMP 0.0001f #define SPEED 0.25f /*使用二维维纹理内存*/ //在GPU上定义纹理变量 text

wufenxia的专栏 1691

CUDA总结:纹理内存

来源:《CUDA_Runtime_API》、《CUDA_C_Programming_Guide-V8.0》 纹理内存和表面内存(surface memory)实质上是全局内存的一个特殊形态,全局内存被绑定为纹理内存(表面内存),对其的读(写)操作将通过专门的texture cache(纹理缓存)进行,其实称为纹理缓存更加贴切。 纹理缓存的优势:纹理缓存具备硬件插值特性,可以实现最近邻插值和线性

Kelvin_Yan的专栏 1万+

CUDA By Examples 8 - 纹理内存Texture Memory

可以使用一维和二维纹理内存. 纹理内存是只读的.

Full_Speed_Turbo 1511

CUDA内存--纹理内存的说明与使用

纹理介绍      纹理存储器(texture memory)是一种只读存储器,由GPU用于纹理渲染的的图形专用单元发展而来,因此也提供了一些特殊功能。      纹理缓存的优势:纹理缓存具备硬件插值特性,可以实现最近邻插值和线性插值。纹理缓存针对二维空间的局部性访问进行了优化,所以通过纹理缓存访问二维矩阵的邻域会获得加速。纹理缓存不需要满足全局内存的合并访问条件。      纹理可以是一段

学习使我快乐 9297

NVIDIA CUDA 学习 (5) Texture Memory

Texture Memory纹理内存热传导模型 纹理内存 热传导模型

weixin_47658743的博客 1004

《cuda by example》 book.h 头文件解析

cpu_bitmap.h 头函数解析                blog.youkuaiyun.com/ghost129/article/details/4409565 都是一些函数,等用到的时候再过来解析,哈哈! #ifndef __BOOK_H__ #define __BOOK_H__ #include /* cuda检错程序 */ static

fulva的专栏 6167

CUDA + OpenCV实现CUDA By Example中波纹示例

参考了网上其他人的CUDA + OpenCV实现Julia Set的例子( https://blog.youkuaiyun.com/Ricardo232525/article/details/74178963),用到了波纹示例上,代码如下:#include "device_launch_parameters.h"#include "cuda_runtime.h"  #include &lt;highgui.h...

小小学渣的博客 1225

CUDA 内存模型

CUDA 内存模型 CUDA 可编程内存类型: 寄存器(Registers) 共享内存(Shared Memory) 本地内存(Local Memory) 常量内存(Constant Memory) 纹理内存(Texture Memory) 全局内存(Global Memory) 上述各种内存都有自己的作用域,生命周期和缓存行为。一个核函数中的每个线程都有自己私有的本地内存。一个线程块有自己的共享内存,对同一线程块内所有线程可见,其内容持续线程块的整个生命周期。所有线程都可以访问全局内存。所有线程都能访

A-Egoist的博客 1195

CUDA(Ⅷ):纹理内存(Texture Memory)

1.学习目标: 了解纹理内存的性能特性 了解如何在CUDA C中使用一维纹理内存 了解如何在CUDA C中使用二维纹理内存 2.背景提出: 特定环境中使用特殊的内存将极大地提升应用程序的性能。与Constant Memory相似,Texture Memory同样缓存在芯片上,只读内存,在某些情况下,能减少对内存的请求并提供更高效的内存带宽。Texture Mmeory是专门为那些在内存访...

每天进步一点点! 7386
上一篇: CUDA by example Chapter6 常量内存与事件
下一篇: CUDA by example Chapter9 原子性
Leo-Ma
博客等级 码龄11年 1701粉丝 124原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值