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

news2024/9/28 13:19:04

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

  • 1.完整代码
  • 2.原理介绍
  • 2.1 将各block 线程对应元素放入共享内存tile
  • 2.2 实现转置
  • 2.3 在此基础上修改
  • 参考文献

本文参考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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/349447.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

表情包可视化编辑、生成配置信息数据工具

合成GIF图片 - 表情包 后续&#xff0c;用于快速、便捷生成 img_config.js 中 要生成的GIF每一帧数据&#xff08;写入头像图片信息参数&#xff09;&#xff1b; 1、先上传 写入GIF中头像 标准图&#xff0c;同时获取图片信息&#xff0c;更新 写入GIF中头像 初始值&#xff0…

5-HT2A靶向药物|适应症|市场销售-上市药品前景分析

据世界卫生组织称&#xff0c;抑郁症是一种多因素疾病&#xff0c;影响全球约3.5 亿人。中枢神经系统最广泛的单胺 - 血清素 (5-HT) 被认为在这种情况的病理机制中起着至关重要的作用&#xff0c;并且神经递质的重要性被“血清素假说”提升&#xff0c;将抑郁症的存在联系起来 …

配置Qt Creator

前言 为了使Qt Creator更像您最喜欢的代码编辑器或IDE&#xff0c;您可以更改键盘快捷键、配色方案、通用高亮显示、代码片段和版本控制系统的设置。 检查生成和运行设置 Qt Creator是一个集成开发环境(IDE)&#xff0c;可以用来开发Qt应用程序。虽然您可以使用Qt Installer…

聊天不发表情包会不习惯吗,Python带你轻松采集上万个表情包

前言 (&#xff61;&#xff65;∀&#xff65;)&#xff89;&#xff9e;嗨 大家好&#xff0c;这里是小圆 聊天没表情包你会有点不习惯的感jio吗 就比如新注册了个qq或者微信再或者企业微信 emmm我就是这样拿到新账号后&#xff0c;跟别人聊聊天想发送表情包 &#xff0c;…

C++-类和对象(下)

C-类和对象&#xff08;下&#xff09;一&#xff0c;const成员函数二&#xff0c;再谈构造函数1&#xff0c;初始化列表2&#xff0c;explicit关键字三&#xff0c;static成员四&#xff0c;友元&#xff08;friend&#xff09;1&#xff0c;全局函数做友元2&#xff0c;类做友…

冷冻电镜 - ChimeraX Density Map 密度图 操作

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/129055160 由冷冻电镜所生成的Volume,需要观察其内部结构,使用ChimeraX进行操作。 加载Volumes,例如my_volume.mrc 效果如下: 高斯滤波 在命令行(Co…

python 数据分析可视化实战 超全 附完整代码数据

代码数据&#xff1a;https://download.csdn.net/download/qq_38735017/873799141.1 数据预处理1.1.1 异常值检测①将支付时间转为标准时间的过程中发生错误&#xff0c;经排查错误数据为‘2017/2/29’,后将其修改为‘2017/2/27’。②经检测发现部分订单应付金额与实付金额都为…

解决jupyter以及windows系统中pycharm编译器画图的中文乱码问题大全

一、jupyter环境下中文乱码问题解决 我们在jupyter的notebook中使用matplotlib画图的时候&#xff0c;经常性的会遇见一些中文乱码显示□的情况,如下所示: 在此&#xff0c;网上给出的方法大多是以下的解决方法&#xff1a; import matplotlib.pyplot as pltplt.rcParams[fo…

界面组件Telerik UI for WinForms R1 2023——全新的Windows 11主题

Telerik UI for WinForms拥有适用Windows Forms的110多个令人惊叹的UI控件。所有的UI for WinForms控件都具有完整的主题支持&#xff0c;可以轻松地帮助开发人员在桌面和平板电脑应用程序提供一致美观的下一代用户体验。Telerik UI for WinForms组件发布了2023年第一个重大版本…

QCon演讲实录(下):多云管理关键能力实现与解析-AppManager

在上篇中&#xff0c;我们已经基本了解了多云管理。现在&#xff0c;我们将深入探讨多云管理关键能力实现&#xff1a;AppManager。 什么是AppManager&#xff1f; 上面我们讲了理论、我们自己使用的交付流程和整体架构&#xff0c;下面我们进入关键能力实现与解析的环节&…

Allegro如何通过视图显示区分动态和静态铜皮操作指导

Allegro如何通过视图显示区分动态和静态铜皮操作指导 用Allegro做PCB设计的时候,通常动态和静态铜皮是无法通过视图显示区分的,只能通过show element查看得知,如下图 左边铜皮是动态铜皮,右边是静态铜皮 但Allegro可以通过一些设置让动静态铜皮以不同效果显示出来 具体操…

Elasticsearch:使用 intervals query - 根据匹配项的顺序和接近度返回文档

Intervals query 根据匹配项的顺序和接近度返回文档。Intervals 查询使用匹配规则&#xff0c;由一小组定义构成。 然后将这些规则应用于指定字段中的术语。 这些定义产生跨越文本正文中的术语的最小间隔序列。 这些间隔可以通过父源进一步组合和过滤。 上述描述有点费解。我…

【计算机网络】HTTPS协议原理

文章目录一、认识HTTPS协议二、为什么要发明HTTPS三、HTTP与HTTPS的区别四、常见的加密方式1. 对称加密2. 非对称加密3. 数据摘要4. 数字签名五、HTTPS的原理探究方案1&#xff1a;只使用对称加密方案2&#xff1a;只使用非对称加密方案3&#xff1a;双方都使用非对称加密方案4…

2.15学习总结

上次被学长的问题给问住了&#xff0c;突然发现自己动规有点糊涂&#xff0c;然后就去屁颠屁颠的复习&#xff0c;找几个之前做过的题&#xff0c;突然发现&#xff0c;竟然还是写了好久才写出来&#xff0c;怎么说呢&#xff0c;信心被强烈打击到&#xff0c;然后自己找了一个…

MyBatis 之二(增、删、改操作)

文章目录1. 修改操作1.1 在 mapper&#xff08;interface&#xff09;里面添加修改方法的声明1.2 在 XMl 中添加 <update> 标签和修改的 sql 代码1.3 在 UserMapper 中右键 Generate 点击 Test 生成 update 测试类2. 删除操作2.1 在 mapper &#xff08;interface&#x…

重生之我是赏金猎人-SRC漏洞挖掘(六)-记一次有趣的客户端RCE+服务端XXE挖掘

0x01 起因 朋友给某甲方做渗透测试&#xff0c;奈何甲方是某知名保险&#xff0c;系统太耐艹&#xff0c;半天不出货 兄弟喊我来一块来看&#xff0c;于是有了本文 0x02 客户端RCE一处 朋友把靶标发给我看了下&#xff0c;除了两个下载链接啥也没有 链接下载下来的东西如图…

回溯算法理论基础

目录什么是回溯法回溯法的效率回溯法解决的问题如何理解回溯法回溯法模板什么是回溯法 回溯法也可以叫做回溯搜索法&#xff0c;它是一种搜索的方式。 回溯是递归的副产品&#xff0c;只要有递归就会有回溯。 所以以下讲解中&#xff0c;回溯函数也就是递归函数&#xff0c;指…

SpringCloud: sentinel降级配置、热点参数、系统规则 配置到nacos

一、application.yml spring.cloud.sentinel cloud:nacos:discovery:# 服务注册地址server-addr: xxx.xxx.xxx.xxx:8848sentinel:eager: truetransport:# 控制台地址dashboard: localhost:9999# nacos配置持久化datasource:ds2:nacos:server-addr: xxx.xxx.xxx.xxx:8848dataId…

YOLO 格式数据集制作

目录 1. YOLO简介 2.分割数据集准备 3.代码展示 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01; 1. YOLO简介 YOLO&#xff08;You Only Look Once&#xff09;是一种流行的目标检测和图像分割模型&#xff0c;由华盛顿大学的 Joseph Redmon 和 Al…

B端产品设计表单的主要分类和相关控件认识

在 Ant、TDesign、Arco 等开源系统中&#xff0c;表单的控件罗列、解释都已经非常全面了&#xff0c;即使是新手完整的看一遍&#xff08;这可不能偷懒&#xff5e;&#xff09;&#xff0c; 也能对表单相关控件有个大致的认识了。 之所以还要更新今天这篇内容&#xff0c;就是…