【CUDA】 1D卷积 1D Convolution

卷积操作

卷积是一种在许多应用中使用的热门数组操作。它被用于图像处理、信号处理和机器学习。本文介绍1D卷积的CUDA实现,2D卷积请参考【CUDA】 2D卷积 2DConvolution

图1展示了一个卷积操作的示例:

图1

基本方法、常数存储器、共享存储器和Caching

基本方法

在基本方法中,卷积是通过逐次访问数组并为每个元素计算卷积来计算的。这种方法很简单,但效率不高。原因在于卷积操作需要多次访问输入数组和mask (掩膜,卷积核,掩码)。

常量存储器

为了避免多次访问mask ,我们可以将mask 的元素存储到常量内存中。这将使硬件能够将mask 元素缓存到L2缓存内存中。这将大大减少全局内存访问,并提高kernel的性能。

共享存储器

共享存储器方法利用共享内存缓存输入数组。由于卷积操作多次访问相同的输入元素,我们可以将输入元素的块缓存到共享内存中,这样相邻的线程就不会从全局内存中加载相同的元素。

Caching

Caching处理方式是共享存储器处理方式的一个变种。唯一的区别是将halo(边角)元素如图2从全局内存中访问。我们之所以这样做是因为halo单元来自相邻块的内部单元。这意味着这些元素可能已经在L2缓存中找到,这将使我们再次避免全局内存访问的同时简化代码。

图2

以上四种方法再介绍完Code后分析访存差异。

Code

Host代码用随机值初始化输入向量和mask,并调用kernel执行1D卷积。

#include <iostream>
#include <cstdio>
#include <ctime>
#include <cmath>

#include <cuda_runtime.h>
#include <thrust/host_vector.h>
#include <thrust/device_vector.h>
#include "Convolution1D.cuh"
#include "helper_cuda.h"
#include "error.cuh"

const double EPSILON = 1.0e-10;
const int FORTIME = 50;



void check_res(float* h_out, float* d_out, int img_w, std::string kernel_name) {
	bool success = true;
	for (int i = 0; i < img_w; ++i) {
		if (fabs(h_out[i] - d_out[i]) > 0.001) {
			std::cout << "Error at " << i << ": " << h_out[i] << " != " << d_out[i] << std::endl;
			success = false;
		}
	}
	std::cout << "Test (" << kernel_name << "): " << (success ? "PASSED" : "FAILED") << std::endl;
}


int main(void)
{
	int img_w, mask_w, tile_w, mask_w_radius;

	img_w = 4194304;
	//img_w = 1024;
	mask_w = 25;
	tile_w = 1024;

	mask_w_radius = mask_w / 2;

	thrust::host_vector<float> h_img(img_w);
	thrust::host_vector<float> h_mask(mask_w);
	thrust::host_vector<float> h_out(img_w);
	thrust::host_vector<float> h_dout(img_w);

	srand(time(NULL));
	for (int i = 0; i < img_w; ++i)
		h_img[i] = (rand() % 256) / 255.0;

	for (int i = 0; i < mask_w; ++i)
		h_mask[i] = (rand() % 256) / 255.0 / (mask_w / 4.);


	for (int i = 0; i < img_w; ++i) {
		for (int m = 0; m < mask_w; ++m)
			if (i + m - mask_w_radius >= 0 && i + m - mask_w_radius < img_w)
				h_out[i] += h_img[i + m - mask_w_radius] * h_mask[m];
		h_out[i] = clamp(h_out[i]);
	}

	thrust::device_vector<float> d_img = h_img;
	thrust::device_vector<float> d_mask = h_mask;
	thrust::device_vector<float> d_out(img_w);

	

	int block_dim = tile_w;
	int grid_dim = (img_w + block_dim - 1) / block_dim;
	
	cudaEvent_t start, stop;
	checkCudaErrors(cudaEventCreate(&start));
	checkCudaErrors(cudaEventCreate(&stop));
	checkCudaErrors(cudaEventRecord(start));
	for (int i = 0; i < FORTIME; i++) {
		convolution_1D_bas
好的,我可以帮你解答这个问题。首先,我们需要明确以下几个概念: - 1D卷积操作:将一个一维的输入信号和一个一维的卷积核进行卷积操作,得到一个一维的输出信号。 - 共享内存:在CUDA程序中,共享内存是一种高速缓存,可以被所有线程访问。使用共享内存可以减少全局内存的访问次数,从而提高程序的性能。 - Tiles:在CUDA程序中,Tiles是一种将输入数据分块的技术。每个Tile的大小通常是32x32或者16x16,在每个Tile内部使用共享内存进行数据传输和计算,从而减少全局内存的访问次数,提高程序的性能。 - 第三种策略:在CUDA程序中,有三种主要的共享内存访问策略:分块、循环展开和递归展开。第三种策略是一种递归展开的策略,它可以在保证共享内存不溢出的前提下,尽可能多地利用共享内存。 下面是一个简单的示例代码,演示了如何使用共享内存和Tiles,实现1D卷积操作,并使用第三种策略: ```c __global__ void convolution_1d(float* input, float* output, float* kernel, int input_size, int kernel_size) { // 每个Tile的大小为32,每个线程处理一个像素 const int tile_size = 32; __shared__ float input_tile[tile_size + kernel_size - 1]; // 计算线程的索引和Tile的索引 int tid = threadIdx.x; int tile_idx = blockIdx.x * tile_size; // 将输入数据复制到共享内存 for (int i = tid; i < tile_size + kernel_size - 1; i += blockDim.x) { int input_idx = tile_idx + i - kernel_size / 2; if (input_idx >= 0 && input_idx < input_size) { input_tile[i] = input[input_idx]; } else { input_tile[i] = 0.0f; } } __syncthreads(); // 计算输出数据 float sum = 0.0f; for (int i = 0; i < kernel_size; i++) { sum += input_tile[tid + i] * kernel[i]; } // 将输出数据写回到全局内存 int output_idx = tile_idx + tid; if (output_idx < input_size) { output[output_idx] = sum; } } ``` 在这个示例代码中,我们使用了一个32x1的Tile,并将输入数据复制到共享内存中。然后,每个线程计算一个输出像素的值,并将其写回到全局内存中。在每个Tile内部,我们使用了第三种策略,即递归展开的策略,从而尽可能多地利用共享内存。 请注意,这个示例代码并没有考虑边界条件和卷积核的大小。在实际应用中,您需要根据具体的情况,对代码进行适当的修改和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值