【参加CUDA线上训练营】共享内存实例1:矩阵转置实现及其优化①

2023-11-13

【参加CUDA线上训练营】共享内存实例1:矩阵转置实现及其优化①

本文参考Nvidia官方blog[An Efficient Matrix Transpose in CUDA C/C++及其对应的github代码transpose.cu学习下共享内存(Shared Memory)的使用,感受下其加速效果。

使用的共享内存大小为2*2的tile,一个block中定义的线程数也是2*2。这也是本文与共享内存实例1:矩阵转置实现及其优化②的主要区别。

1.完整代码

#include "error.cuh"
#include <stdio.h>

#ifdef USE_DP
    typedef double real;
#else
    typedef float real;
#endif

const int NUM_REPEATS = 10;
const int TILE_DIM = 32;

void timing(const real *d_A, real *d_B, const int N, const int task);
__global__ void transpose1(const real *A, real *B, const int N);
__global__ void transpose2(const real *A, real *B, const int N);
void print_matrix(const int N, const real *A);

int main(int argc, char **argv)
{
    if (argc != 2)
    {
        printf("usage: %s N\n", argv[0]);
        exit(1);
    }
    const int N = atoi(argv[1]);

    const int N2 = N * N;
    const int M = sizeof(real) * N2;
    real *h_A = (real *) malloc(M);
    real *h_B = (real *) malloc(M);
    for (int n = 0; n < N2; ++n)
    {
        h_A[n] = n;
    }
    real *d_A, *d_B;
    CHECK(cudaMalloc(&d_A, M));
    CHECK(cudaMalloc(&d_B, M));
    CHECK(cudaMemcpy(d_A, h_A, M, cudaMemcpyHostToDevice));

    printf("\ntranspose with shared memory bank conflict:\n");
    timing(d_A, d_B, N, 1);
    printf("\ntranspose without shared memory bank conflict:\n");
    timing(d_A, d_B, N, 2);

    CHECK(cudaMemcpy(h_B, d_B, M, cudaMemcpyDeviceToHost));
    if (N <= 10)
    {
        printf("A =\n");
        print_matrix(N, h_A);
        printf("\nB =\n");
        print_matrix(N, h_B);
    }

    free(h_A);
    free(h_B);
    CHECK(cudaFree(d_A));
    CHECK(cudaFree(d_B));
    return 0;
}

void timing(const real *d_A, real *d_B, const int N, const int task)
{
    const int grid_size_x = (N + TILE_DIM - 1) / TILE_DIM;
    const int grid_size_y = grid_size_x;
    const dim3 block_size(TILE_DIM, TILE_DIM);
    const dim3 grid_size(grid_size_x, grid_size_y);

    float t_sum = 0;
    float t2_sum = 0;
    for (int repeat = 0; repeat <= NUM_REPEATS; ++repeat)
    {
        cudaEvent_t start, stop;
        CHECK(cudaEventCreate(&start));
        CHECK(cudaEventCreate(&stop));
        CHECK(cudaEventRecord(start));
        cudaEventQuery(start);

        switch (task)
        {
            case 1:
                transpose1<<<grid_size, block_size>>>(d_A, d_B, N);
                break;
            case 2:
                transpose2<<<grid_size, block_size>>>(d_A, d_B, N);
                break;
            default:
                printf("Error: wrong task\n");
                exit(1);
                break;
        }

        CHECK(cudaEventRecord(stop));
        CHECK(cudaEventSynchronize(stop));
        float elapsed_time;
        CHECK(cudaEventElapsedTime(&elapsed_time, start, stop));
        printf("Time = %g ms.\n", elapsed_time);

        if (repeat > 0)
        {
            t_sum += elapsed_time;
            t2_sum += elapsed_time * elapsed_time;
        }

        CHECK(cudaEventDestroy(start));
        CHECK(cudaEventDestroy(stop));
    }

    const float t_ave = t_sum / NUM_REPEATS;
    const float t_err = sqrt(t2_sum / NUM_REPEATS - t_ave * t_ave);
    printf("Time = %g +- %g ms.\n", t_ave, t_err);
}

__global__ void transpose1(const real *A, real *B, const int N)
{
    __shared__ real S[TILE_DIM][TILE_DIM];
    int bx = blockIdx.x * TILE_DIM;
    int by = blockIdx.y * TILE_DIM;

    int nx1 = bx + threadIdx.x;
    int ny1 = by + threadIdx.y;
    if (nx1 < N && ny1 < N)
    {
        S[threadIdx.y][threadIdx.x] = A[ny1 * N + nx1];
    }
    __syncthreads();

    int nx2 = bx + threadIdx.y;
    int ny2 = by + threadIdx.x;
    if (nx2 < N && ny2 < N)
    {
        B[nx2 * N + ny2] = S[threadIdx.x][threadIdx.y];
    }
}

__global__ void transpose2(const real *A, real *B, const int N)
{
    __shared__ real S[TILE_DIM][TILE_DIM + 1];
    int bx = blockIdx.x * TILE_DIM;
    int by = blockIdx.y * TILE_DIM;

    int nx1 = bx + threadIdx.x;
    int ny1 = by + threadIdx.y;
    if (nx1 < N && ny1 < N)
    {
        S[threadIdx.y][threadIdx.x] = A[ny1 * N + nx1];
    }
    __syncthreads();

    int nx2 = bx + threadIdx.y;
    int ny2 = by + threadIdx.x;
    if (nx2 < N && ny2 < N)
    {
        B[nx2 * N + ny2] = S[threadIdx.x][threadIdx.y];
    }
}

void print_matrix(const int N, const real *A)
{
    for (int ny = 0; ny < N; ny++)
    {
        for (int nx = 0; nx < N; nx++)
        {
            printf("%g\t", A[ny * N + nx]);
        }
        printf("\n");
    }
}

2.原理介绍

核函数transpose1和transpose2主要区别在于消除Bank Conflicts(更详细的信息可见共享内存实例1:矩阵转置实现及其优化②),这里就transpose1进行下分析。

__global__ void transpose1(const real *A, real *B, const int N)
{
    __shared__ real S[TILE_DIM][TILE_DIM];
    int bx = blockIdx.x * TILE_DIM;
    int by = blockIdx.y * TILE_DIM;

    int nx1 = bx + threadIdx.x;
    int ny1 = by + threadIdx.y;
    if (nx1 < N && ny1 < N)
    {
        S[threadIdx.y][threadIdx.x] = A[ny1 * N + nx1];
    }
    __syncthreads();

    int nx2 = bx + threadIdx.y;
    int ny2 = by + threadIdx.x;
    if (nx2 < N && ny2 < N)
    {
        B[nx2 * N + ny2] = S[threadIdx.x][threadIdx.y];
    }
}

以6*6矩阵的转置为例:

2.1 将各block 线程对应元素放入共享内存tile

前半部分代码主要实现的是将各block 线程对应元素放入对应的共享内存tile。

S[threadIdx.y][threadIdx.x] = A[ny1 * N + nx1];

在这里插入图片描述

2.2 实现转置

    int bx = blockIdx.x * TILE_DIM;
    int by = blockIdx.y * TILE_DIM;
    .
    .
    .
    int nx2 = bx + threadIdx.y;
    int ny2 = by + threadIdx.x;
    if (nx2 < N && ny2 < N)
    {
        B[nx2 * N + ny2] = S[threadIdx.x][threadIdx.y];
    }

以右上角这个block中的数据为例,转置过程如下图所示:
在这里插入图片描述
首先,对于block的id,

转置前:

Blockid_x   Blockid_y  
 2             0             

转置后:

Blockid_x   Blockid_y  
 0             2                          

threadIdx.xthreadIdx.y为什么不需要反过来呢?

int bx = blockIdx.x * TILE_DIM;
int by = blockIdx.y * TILE_DIM;
.
.
.
int nx2 = bx + threadIdx.y;
int ny2 = by + threadIdx.x;

这是因为,单独看每个block,已经通过下面这句代码实现了各个块中对应元素的转置:

B[nx2 * N + ny2] = S[threadIdx.x][threadIdx.y];

#正常读取是这样的:S[threadIdx.y][threadIdx.x], 
#而S[threadIdx.x][threadIdx.y]正好反过来。

2.3 在此基础上修改

    //int nx2 = bx + threadIdx.y;
    //int ny2 = by + threadIdx.x;
    //if (nx2 < N && ny2 < N)
    //{
    //    B[nx2 * N + ny2] = S[threadIdx.x][threadIdx.y];
    //}
	if (nx1 < N && ny1 < N)
    {
	B[nx1 * N + ny1] = S[threadIdx.y][threadIdx.x];
    }

原代码使用了block id_x和id_y对调,共享内存中thread id_x和id_y两步实现,思路和实现都比较复杂,可读性也比较差,此代码对block id及block thread id进行对调。

经咨询,此代码没有合并,起不到加速效果

参考文献

[1] transpose.cu
[2] brucefan1983/CUDA-Programming/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【参加CUDA线上训练营】共享内存实例1:矩阵转置实现及其优化① 的相关文章

  • 如何在 CUDA 中执行多个矩阵乘法?

    我有一个方阵数组int M 10 以便M i 定位第一个元素i th 矩阵 我想将所有矩阵相乘M i 通过另一个矩阵N 这样我就收到了方阵数组int P 10 作为输出 我看到有不同的可能性 分配不同元素的计算M i 到不同的线程 例如 我
  • __syncthreads() 死锁

    如果只有部分线程执行 syncthreads 会导致死锁吗 我有一个这样的内核 global void Kernel int N int a if threadIdx x
  • 加速Cuda程序

    要更改哪一部分来加速此代码 代码到底在做什么 global void mat Matrix a Matrix b int tempData new int 2 tempData 0 threadIdx x tempData 1 blockI
  • 在 __device/global__ CUDA 内核中动态分配内存

    根据CUDA 编程指南 http developer download nvidia com compute cuda 3 2 prod toolkit docs CUDA C Programming Guide pdf 第 122 页 可
  • cuda中内核的并行执行

    可以说我有三个全局数组 它们已使用 cudaMemcpy 复制到 GPU 中 但 c 中的这些全局数组尚未使用 cudaHostAlloc 分配 以便分配页面锁定的内存 而不是简单的全局分配 int a 100 b 100 c 100 cu
  • CUDA - 将 CPU 变量传输到 GPU __constant__ 变量

    与 CUDA 的任何事情一样 最基本的事情有时也是最难的 所以 我只想将变量从 CPU 复制到 GPUconstant变量 我很难过 这就是我所拥有的 constant int contadorlinhasx d int main int
  • CUDA 中指令重放的其他原因

    这是我从 nvprof CUDA 5 5 获得的输出 Invocations Metric Name Metric Description Min Max Avg Device Tesla K40c 0 Kernel MyKernel do
  • 具有 Cuda Thrust 的多个 GPU?

    如何将 Thrust 与多个 GPU 一起使用 这只是使用 cudaSetDevice deviceId 的问题吗 然后运行相关的 Thrust 代码 使用 CUDA 4 0 或更高版本 cudaSetDevice deviceId 接下来
  • cuda 文件组织的有效方式:.cpp .h .cu .cuh .curnel 文件

    cuda最容易理解 最高效的代码组织是什么 经过一番调查后 我发现 cuda 函数声明应位于 cuh 文件中 实现位于 cu 文件中 内核函数实现位于 curnel 文件中 其他 C 内容通常在 cpp 和 h 文件中 最近我发布了一个问题
  • CUDA Thrust 的多 GPU 使用

    我想使用我的两张显卡通过 CUDA Thrust 进行计算 我有两张显卡 在单卡上运行对于两张卡都适用 即使我在 std vector 中存储两个 device vector 也是如此 如果我同时使用两张卡 循环中的第一个周期将起作用并且不
  • 如何在CUDA应用程序中正确应用线程同步?

    一般来说 我在应用程序中偶尔会使用线程同步 因为我并不经常需要此功能 我并不是真正的高级 C C 程序员 但我也不是初学者 我开始学习 CUDA C 对当今 GPU 与 CPU 的能力相比感到兴奋 我意识到 CUDA 编程主要是关于并行线程
  • 无法在 CUDA 中找到 1 到 100 数字的简单和?

    我正在研究使用 CUDA 的图像处理算法 在我的算法中 我想使用 CUDA 内核找到图像所有像素的总和 所以我在cuda中制作了内核方法 来测量16位灰度图像的所有像素的总和 但我得到了错误的答案 所以我在cuda中编写了一个简单的程序来查
  • 为什么 cudaGLSetGLDevice 失败,即使它是在 main 函数的第一行中调用的

    我想使用 OpenGL 和 CUDA 之间的互操作性 我知道 正如一些教程所说 第一步是选择设备 但是 当我在主函数的第一行中调用 cudaGLSetGLDevice 0 时 程序退出并显示信息 cudaSafeCall 运行时 API 错
  • CUDA 和 Eigen 的成员“已声明”错误

    我只是 CUDA 和 Nsight 的初学者 希望利用出色的 GPU 性能进行线性代数运算 例如 CUBLAS 我在以下人员的帮助下编写了很多自定义代码Eigen http eigen tuxfamily org index php tit
  • 如何在没有 nvcc 的情况下在编译时获取 CUDA 工具包版本?

    我在 cpp 文件中对 cuSPARSE 库进行了一些调用 这些调用在旧工具包中不可用 为了支持使用旧工具包的系统 我想使用编译器指令编译不同的代码部分 特别是 我想使用旧工具包的 CSR 格式矩阵和新工具包的 BSR 格式矩阵来求解稀疏三
  • CUDA-Kernel 应该根据块大小动态崩溃

    我想做稀疏矩阵 密集向量乘法 假设用于压缩矩阵中条目的唯一存储格式是压缩行存储 CRS 我的内核如下所示 global void krnlSpMVmul1 float data mat int num nonzeroes unsigned
  • 完全禁用 NVCC 优化

    我正在尝试测量 GPU 上的峰值单精度触发器 为此我正在修改 PTX 文件以在寄存器上执行连续的 MAD 指令 不幸的是 编译器正在删除所有代码 因为它实际上没有做任何有用的事情 因为我没有执行任何数据的加载 存储 是否有编译器标志或编译指
  • CUDA 代码会损坏 GPU 吗?

    在测试包含内存错误的 CUDA 时 我的屏幕被冻结了 重新启动后我无法再检测到显卡 我的代码是否有可能物理损坏该卡 这发生在 Ubuntu 14 04 下 我不知道该卡的型号 因为我无法检测到它 但我记得它是一张相当新的卡 感谢所有的评论我
  • cudaMalloc使用向量>进行管理 > C++ - NVIDIA CUDA

    我正在通过 NVIDIA GeForce GT 650M GPU 为我创建的模拟实现多线程 为了确保一切正常工作 我创建了一些辅助代码来测试一切是否正常 在某一时刻 我需要更新变量向量 它们都可以单独更新 这是它的要点 device int
  • 了解流式多处理器 (SM) 和流式处理器 (SP)

    我正在尝试了解 GPU 的基本架构 我已经阅读了很多材料 包括这个非常好的答案 https stackoverflow com a 2213744 2386113 但我仍然很困惑 无法得到一个好的图片 我的理解 GPU 包含两个或多个流式多

随机推荐

  • 中职网络安全2022国赛之MS12020漏洞扫描与利用(CVE-2012-0002)

    简介 我做了一个简单的环境来复现这个漏洞 需要虚拟机环境的可以加我qq 3316735898 有什么不会的也可以问我 1 在MSF工具中用search命令搜索MS12020 RDP拒绝服务攻击模块 将回显结果中的漏洞披露时间作为Flag值
  • 手把手教你制作一块Linux开发板(基于Planck-pi)

    文章目录 前言 一 前期准备 二 焊接部分 三 镜像烧写部分 总结 前言 攻城狮星河 Hello 各位野生钢铁侠们 这篇文章初衷是帮助想自己制作linux小板子的小白们 文中会讲的比较基础 大佬勿喷 本教程会以稚晖君开源的 planck p
  • Win7下的C语言开发环境

    本文参考至 http jingyan baidu com article 870c6fc32fa08fb03fe4beae html
  • APS自动排产在企业生产中的应用:生产整体优化

    APS系统 又名高级计划与排程 Advanced Planning and Scheduling 企业管理软件 是对所有资源具有同步的 实时的 具有约束能力的 模拟能力 不论是物料 机器设备 人员 供应 客户需求 运输等影响计划因素 不论是
  • java堆,栈,常量池最通俗易懂的图文解释

    转自 http www iteye com topic 634530 1 寄存器 最快的存储区 由编译器根据需求进行分配 我们在程序中无法控制 2 栈 stack 存放基本类型的变量数据和对象的引用 但对象本身不存放在栈中 而是存放在堆 n
  • 顺序栈和链式栈的定义及基本操作(c++实现)

    顺序栈 include
  • 全网最新首发:Python从入门到精通的完整学习路线图【附:全套Python学习资料】

    后台有很多粉丝朋友们留言问我 Python应该怎么学 爬虫和数据分析怎么学 机器学习怎么学 其实python的门槛不是特别高 但是很多朋友感觉很迷茫 学了一段时间还是不入流 很大一部分原因是你没有一个完整的知识体系 你不知道自己现在的进度
  • Spring事务管理--@Transactional

    使用步骤 步骤一 在spring配置文件中引入
  • 零起步教你搭建Discuz!论坛(转载)

    这段时间 拜美国所赐 大家对鲲鹏生态非常关注 特别是基于鲲鹏920cpu的鲲鹏架构服务器 引起了大家的激烈讨论 应该说大部分网友对鲲鹏架构服务器还是持支持态度的 但是部分不太了解具体情况的网友 特别是一些被以前此起彼伏的 伪自主 真诈骗 的
  • 加法电路原理

    任务1 建立简单电路 1 建立非门 通过http ss sysu edu cn pml se121 hardware1 html 进行相关功能操作 模拟电路如下图 2 验证知道 开关打开时LED灯不亮 关闭时显绿光 故此有表格 3 利用XO
  • WebEye云课堂|BigQuery最佳实践

    企业在布局出海发展中想要轻松搞定 大型数据集 却绝非易事 不论是各种繁杂的存储配置 还是调用各类分析工具来正确处理数据 都有可能因为数据集过于庞大而面临各种各样的困难 我们诚邀您参与此次直播活动 您将全面认识到谷歌云PB级数据仓库 BigQ
  • 从Docker到Kubernetes——Docker构建应用栈(二)

    文章目录 App容器节点 Django 的配置 HAProxy容器节点的配置 应用栈访问测试 App容器节点 Django 的配置 Django容器启动后 需要利用Django框架 开发一个简单的Web程序 首先进入Django的容器 执行
  • 用AutoCompleteTextView实现历史记录提示

    这画面不陌生吧 百度的提示 他的词库并不是历史记录 是搜索引擎收集的当前最常搜索的内容 假如我们也要在android的应用实现如上功能怎么做呢 方法很简单 android已经帮我们写好了api 这里就用到了AutoCompleteTextV
  • JavaScript学习笔记—制作网页轮播图

    JavaScript学习笔记 制作网页轮播图 一 分析 构成模块 最外边一个大的div 里头一个ul ul里每个小li放一张图片 核心的滚动区域 左右两个按钮 小圆点 功能需求 鼠标经过轮播图模块 显示左右按钮 离开隐藏左右按钮 动态生成小
  • VCC和GND短路,怎么找问题?

    在调试电路时 可能经常会遇到VCC和GND短路的情况 板子上的VCC和GND点太多了 新手可能觉得不知道从哪找 下面就介绍几种方法 供大家参考 1 目测 最简单的方法 先用肉眼或放大镜观察 尤其是引脚比较密的芯片和封装较小的电容 焊接不好时
  • Tomcat及项目部署

    一 Tomcat是什么 Tomcat 是基于 Java 实现的 个开源免费 也是被 泛使 的 HTTP 服务器 二 下载安装 官 站 https tomcat apache org 选择其中的 zip 压缩包 下载后解压缩即可 解压缩的 录
  • 编译linux内核(一)

    关于linux启动流程 1 第一阶段 BIOS 1 1 硬件自检 1 2 启动顺序 2 第二阶段 主引导记录 2 1 主引导记录的结构 2 2 分区表 3 第三阶段 硬盘启动 3 1 情况A 卷引导记录 3 2 情况B 扩展分区和逻辑分区
  • 图像数据格式uint8与double以及图像类型转换

    1 图像数据格式 double 64位 matlab中数值一般采用double型存储和运算 uint8 8位无符号整数 为了节省存储空间 matlab为图像提供的特殊数据类型 imread把灰度图像存入一个8位矩阵 当为RGB图像时 就存入
  • 慕课第6周在线第2题 计算阶乘的和v2.0

    计算阶乘的和v2 0 4分 题目内容 假设有这样一个三位数m 其百位 十位和个位数字分别是a b c 如果m a b c 则这个三位数就称为三位阶乘和数 约定0 1 请编程计算并输出所有的三位阶乘和数 函数原型 long Fact int
  • 【参加CUDA线上训练营】共享内存实例1:矩阵转置实现及其优化①

    参加CUDA线上训练营 共享内存实例1 矩阵转置实现及其优化 1 完整代码 2 原理介绍 2 1 将各block 线程对应元素放入共享内存tile 2 2 实现转置 2 3 在此基础上修改 参考文献 本文参考Nvidia官方blog An