并行计算之路<2>——CUDA与纹理映射

纹理存储器是GPU常用的优化方法。

举个栗

参考《GPGPU编程技术——从GLSL、CUDA到OpenCL》代码6-2(离散卷积程序)。因为CUDA版本的不同,所以本人对原书的代码进行修改,修改后的代码如下。

#include "cuda_runtime.h"
#include "device_launch_parameters.h"

#include <iostream>

using namespace std;

// Block 的尺寸为[BLOCK_X * BLOCK_Y]
#define BLOCK_X   16u
#define BLOCK_Y   16u

// 定义一个四维向量相加的宏
#define VectorAdd(a, b) a.x += b.x; a.y += b.y; a.z += b.z; a.w += b.w;

// 定义一个四维向量与标量除法的宏
#define VectorScalarDev(a, b, c) a.x = b.x / c; a.y = b.y / c; a.z = b.z / c; a.w = b.w / c;

// 纹理相关全局变量
texture<float4, 2, cudaReadModeElementType> refTex;
cudaArray* cuArray;

__global__ void convolution(int nWidth, int nHeight, int nRadius, float4* pfResult)
{
    // 计算线程号
    const int idxX = blockIdx.x * blockDim.x + threadIdx.x;
    const int idxY = blockIdx.y * blockDim.y + threadIdx.y;

    // 将线程号映射到全局存储器
    const int idxResult = idxY * nHeight + idxX;

    // 近邻元素的数量
    int nTotal = 0;
    // 近邻元素之和
    float4 f4Sum = { 0.0f, 0.0f, 0.0f, 0.0f };
    // 计算结果:输出四元向量
    float4 f4Result = { 0.0f, 0.0f, 0.0f, 0.0f };
    float4 f4Temp = { 0.0f, 0.0f, 0.0f, 0.0f };

    // 近邻元素求和
    for (int ii = idxX - nRadius; ii < idxX + nRadius; ii++)
    {
        for (int jj = idxY - nRadius; jj <= idxY + nRadius; jj++)
        {
            if (ii >= 0 && jj >= 0 && ii < nWidth && jj < nHeight)
            {
                f4Temp = tex2D(refTex, ii, jj);
                VectorAdd(f4Sum, f4Temp);
                nTotal++;
            }
        }
    }
    VectorScalarDev(f4Result, f4Sum, (float)nTotal);
    *(pfResult + idxResult) = f4Result;
}

int main()
{
    unsigned unWidth = 1024u;
    unsigned unHeight = 1024u;
    unsigned unSizeData = unWidth * unHeight;
    unsigned unRadius = 2u;

    // 准备输入数据
    unsigned unData = 0;
    float4* pf4Sampler;
    cudaMallocHost((void **)&pf4Sampler, unSizeData * sizeof(float4));

    for (unsigned i = 0; i < unSizeData; i++)
    {
        pf4Sampler[i].x = (float)(unData++);
        pf4Sampler[i].y = (float)(unData++);
        pf4Sampler[i].z = (float)(unData++);
        pf4Sampler[i].w = (float)(unData++);
    }

    // 准备纹理
    cudaChannelFormatDesc cuDesc = cudaCreateChannelDesc<float4>();
    cudaMallocArray(&cuArray, &cuDesc, unWidth, unHeight);
    cudaMemcpyToArray(cuArray, 0, 0, pf4Sampler, unSizeData * sizeof(float4), cudaMemcpyHostToDevice);

    // 分配全局存储器
    float4* pfResult;
    cudaMalloc((void **)&pfResult, unSizeData * sizeof(float4));

    // 配置线程并调用内核
    dim3 block(BLOCK_X, BLOCK_Y);
    dim3 grid(ceil((float)unWidth / BLOCK_X), ceil((float)unHeight / BLOCK_Y));
    convolution <<< grid, block >>>(unWidth, unHeight, unRadius, pfResult);

    // 结果
    cudaMemcpy(pf4Sampler, pfResult, unSizeData * sizeof(float4), cudaMemcpyDeviceToHost);

    cudaUnbindTexture(refTex);
    cudaFreeHost(pf4Sampler);
    cudaFreeArray(cuArray);
    cudaFree(pfResult);

    return 0;
}

砸开吃

一般使用纹理存储器处理图形(树上是这么说的),至于有什么好处,树上说了一大堆。天书啊( ╯□╰ )看不懂(* ̄︶ ̄)y。不过好歹有个栗子可以砸开吃。

  1. 声明纹理参照系 refTex以及CUDA数组cuArray
  2. 准备输入数据 pf4Sampler
  3. (cudaArray类型本身并非模板,因此在使用cudaMallocArray() 分配存储器空间时调用) cudaChannelFormatDesc() 设置数组的数据类型。cudaMemcpyToArray() 初始化CUDA数组。
  4. 纹理绑定cudaBindTextureToArray()
  5. 纹理拾取tex2D
  6. 数据操作。
  7. 解除绑定。
  8. 释放CUDA数组。
    经过8道工序就个享受纹理的美味了。

在CUDA的Samples中也提供了一个关于纹理的例子simpleTexture.cu。

参考:
《GPGPU编程技术——从GLSL、CUDA到OpenCL》♥♥♥♥♥
《数字图像处理高级应用——基于MATLAB与CUDA的实现》♥♥♥
《基于CUDA的并行程序设计》♥♥♥
《CUDA专家手册》♥♥♥♥♥
《高性能CUDA应用设计与开发》♥♥♥♥

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值