使用 NVProf 检测 CUDA kernel 的 bank conflict

使用 NVProf 检测 CUDA kernel 的 bank conflict

NVProf 指令

使用 NVProf 可以对 bank conflict 进行检测:

nvprof --events shared_ld_bank_conflict,shared_st_bank_conflict <app> [args...]

其中:

  • --events 选项指定的 shared_ld_bank_conflict,shared_st_bank_conflict分别代指从 shared memory 加载(读取)时产生的 bank conflict, 以及向 shared memory 存储(写入)时产生的 bank conflict.
  • <app> [args...] 即要检测的 CUDA 二进制程序及其参数.

额外说明

值得一提的是, 如果没有从 shared memory 读取的指令, 且没有使用 -G 编译, 则两种 bank conflict 事件都无法检测出来, 即使存在向 shared memory 写入产生的 bank conflict.
(没有读取的 bank conflict 很好理解, 因为都没有从 shared memory 读取数据; 而至于写入的 bank conflict, 应该是编译器做了一定的优化, 即 shared memory 虽被写入但数据没有被读取, 则写入是没有意义的, 这部分代码实际并不执行, 所有写入的 bank conflict 就不会检测到了.)

这个主要作用是, 当我们对自己写的 kernel 的 bank conflict 进行检测的时候, 要确保保留对 shared memory 读取的相关代码或设置 -G 编译选项, 否则可能会影响 bank conflict 的检测.

举例

以下代码是一个很简单的 CUDA kernel 示例, 考虑到 bank conflict 是 warp 层面的问题, 所有 kernel 中我定义了 warp_id, land_id 等变量便于后续 bank conflict 的说明.

#include <iostream>
#include <cstdio>
#include <vector>
#include <cuda.h>using namespace std;constexpr int SIZE_A = 64;
constexpr int SIZE_C = 64;__global__ void kernel(const int* a, int* c) {auto tid = (blockIdx.x * blockDim.x + threadIdx.x);auto lane_id = threadIdx.x & 0x1F;auto warp_id = tid >> 5;auto warp_in_block = threadIdx.x >> 5;__shared__ int shm[SIZE_A];if (tid < SIZE_A) {shm[warp_id * 32 + lane_id] = a[warp_id * 32 + lane_id];}if (tid < SIZE_C) {c[warp_id * 32 + lane_id] = shm[warp_id * 32 + lane_id];}
}int main() {vector<int> a(SIZE_A);for (int i = 0; i < SIZE_A; ++i) {a[i] = i;}int* d_a;cudaMalloc(&d_a, sizeof(int) * SIZE_A);cudaMemcpy(d_a, a.data(), sizeof(int) * SIZE_A, cudaMemcpyHostToDevice);int* d_c;cudaMalloc(&d_c, sizeof(int) * SIZE_C);cudaMemset(d_c, 0, sizeof(int) * SIZE_C);kernel<<<1, 128>>>(d_a, d_c);vector<int> c(SIZE_C);cudaMemcpy(c.data(), d_c, sizeof(int) * SIZE_C, cudaMemcpyDeviceToHost);for (auto x : c) {cout << x << " ";}cout << endl;cudaFree(d_c);cudaFree(d_a);return 0;
}

kernel() 函数完成的功能很简单, 就是想数组 a 中的一部分数据先写至 shared memory shm, 再写入到 c 中. 在没有额外说明时, 不使用 -G 选项编译代码.
很明显的是, 由于 shm 的读写时, 每个 warp 的 32 个线程分片读取不同的 4 字节数据, 因此代码没有 bank conflict.
在这里插入图片描述
使用上述 NVProf 指令检测, 结果也印证了上述推断.

现在将 Kernel 修改如下:

__global__ void kernel(const int* a, int* c) {auto tid = (blockIdx.x * blockDim.x + threadIdx.x);auto lane_id = threadIdx.x & 0x1F;auto warp_id = tid >> 5;auto warp_in_block = threadIdx.x >> 5;__shared__ int shm[SIZE_A];// if (tid < SIZE_A) {//     shm[warp_id * 32 + lane_id] = a[warp_id * 32 + lane_id];// }for (auto i = threadIdx.x; i < SIZE_A; i += blockDim.x) {shm[(i % 2) * SIZE_A / 2 + i / 2] = a[i];}if (tid < SIZE_C) {c[warp_id * 32 + lane_id] = shm[warp_id * 32 + lane_id];;}
}

我们在读取 a 数组到 shared memory 的时候, 进行了一点修改. 可以看到, 对应相邻的两个线程, tt+1 (假设 t % 2 ==0), 则一个写入到 shm[t/2], 一个写入到 shm[SIZE_A/2+(t+1)/2]shm[32+t/2], 由于恰好差了 32 个元素, 因此会访问到相同的 bank, 会触发 bank conflict. 通过 NVProf 检测也得到了证实:
在这里插入图片描述
这里的 2 次, 原因笔者猜测为 SIZE_A 大小为 64, 对应 2 个 warp, 每个 warp 相邻的奇数线程和偶数线程访问同一 bank, 以 warp 为单位, 每个 warp 产生 1 个 bank conflict, 共 2 个.

但如果我们将后面将 shm 写入 c 数组的代码注释掉, 即没有从 shared memory 读取的代码, 则可以看到 NVProf 并不会检测到刚刚的 shared_st_bank_conflict.

    if (tid < SIZE_C) {c[warp_id * 32 + lane_id] = shm[warp_id * 32 + lane_id];;}

在这里插入图片描述

但如果我们在编译的时候使用 -G 选项, 则可以看到刚刚的 shared_st_bank_conflict 有可以被检测到了:
在这里插入图片描述

因此, 可以推断出, 在默认情况下, 编译器对于不读取的 shared memory 的写入操作会进行优化, 实际上并不会执行 shared memory 的写入操作, 而 debug 模式 (带 -G 选项)时, 则不会进行该优化.

如下代码展示了在从 shared memory shm 读取到 c 数组时的 bank conflict.

constexpr int SIZE_A = 64;
constexpr int SIZE_C = 32;__global__ void kernel(const int* a, int* c) {auto tid = (blockIdx.x * blockDim.x + threadIdx.x);auto lane_id = threadIdx.x & 0x1F;auto warp_id = tid >> 5;auto warp_in_block = threadIdx.x >> 5;__shared__ int shm[SIZE_A];if (tid < SIZE_A) {shm[warp_id * 32 + lane_id] = a[warp_id * 32 + lane_id];}if (tid < SIZE_C) {// c[warp_id * 32 + lane_id] = shm[warp_id * 32 + lane_id];c[warp_id * 32 + lane_id] =shm[warp_in_block * 32 + lane_id / 8 + (lane_id % 2) * 32];}
}

可以看到, 相邻的 8 个线程分奇偶访问同一 bank 的两个地址. NVProf 输出如下:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NSGA-II求解微电网多目标优化调度(MATLAB)

一、NSGA-II简介 NSGA-Ⅱ算法是Kalyanmoy Deb等人于 2002年在 NSGA 的基础上提出的&#xff0c;它比 NSGA算法更加优越&#xff1a;它采用了快速非支配排序算法&#xff0c;计算复杂度比 NSGA 大大的降低&#xff1b;采用了拥挤度和拥挤度比较算子&#xff0c;代替了需要指定的…

Python Selenium 之数据驱动测试的实现!

数据驱动模式的测试好处相比普通模式的测试就显而易见了吧&#xff01;使用数据驱动的模式&#xff0c;可以根据业务分解测试数据&#xff0c;只需定义变量&#xff0c;使用外部或者自定义的数据使其参数化&#xff0c;从而避免了使用之前测试脚本中固定的数据。可以将测试脚本…

【2023.11.23】JDBC基本连接语法学习➹

1.导入jar包依赖&#xff1a;mysql-connector-java-8.0.27.jar 2.连接数据库&#xff01; 3.无法解析类->导入java.sql.*&#xff0c;&#xff08;将项目方言改为Mysql&#xff09; JDBC&#xff0c;启动&#xff01;&#xff01; public class Main {public static voi…

Shopee本土号封号几率大吗?如何避免封号?被封号了怎么办?

Shopee是近几年热门的电商平台之一&#xff0c;即使越来越多的跨境电商涌现&#xff0c;他的地位在东南亚市场依然占据一席之地&#xff0c;也依旧吸引着需要跨境商家入局。尤其在2023年&#xff0c;在TikTok Shop在印尼被关停之后&#xff0c;留下了大片空白&#xff0c;Shope…

FPGA----ZCU106使用petalinux 2019.1的第一个app开发

1、petalinux在zcu106上的构建参见前文 FPGA----ZCU106使用petalinux 2019.1&#xff08;全网最详&#xff09;-CSDN博客文章浏览阅读31次。本文完成了Vivado 2019.1版本下的基于ZCU106的全部linux系统移植https://blog.csdn.net/qq_37912811/article/details/1345197352、我们…

Modbus RTU协议及modbus库函数使用

一、与Modbus TCP的区别 在一般工业场景使用modbus RTU的场景还是更多一些&#xff0c;modbus RTU基于串行协议进行收发数据&#xff0c;包括RS232/485等工业总线协议。 与modbus TCP不同的是RTU没有报文头MBAP字段&#xff0c;但是在尾部增加了两个CRC检验字节&#xff08;CRC…

【C++】Cmake使用教程(看这一篇就够了)

文章目录 引言一 环境搭建二 简单入门2.1 项目结构2.2 示例源码2.3 运行查看 三 编译多个源文件3.1 在同一个目录下有多个源文件3.1.1 简单版本3.1.1.1 项目结构3.1.1.2 示例代码3.1.1.3 运行查看 3.1.2 进阶版本3.1.2.1 项目结构3.1.2.2 示例源码3.1.2.3 运行查看 3.2 在不同目…

Visual studio的安装教程2022最新版(图文详细)新手小白C语言软件的安装

目录 1、Visual Studio下载地址 2、工作负荷选择“使用C的桌面开发”和”Visual Studio扩展开发“&#xff0c;安装位置自选 3、下载完成后&#xff0c;点击左上角”文件“->”新建“->”项目“->”空项目“ 4、找到解决方案资源管理器的“头文件”&#xff0c;右键…

Visio学习笔记

1. 常用素材 1.1 立方体&#xff1a;张量, tensor 操作路径&#xff1a;更多形状 ⇒ 常规 ⇒ 基本形状 自动配色 在选择【填充】后Visio会自动进行配色&#xff1b;

Git本地库操作

对本地库的操作很少&#xff0c;我们学习1~6节即可&#xff0c;其他了解下。我们可以在idea中完成对本地库还有远程库的操作&#xff0c;可视化界面用起来更加舒适而且也不会混淆。 1. Git概述 Git 是一个免费的、开源的分布式版本控制系统&#xff0c;可以快速高效地处理从小…

飞翔的小鸟小游戏

主类 package APP;import 框架.GameFrame;public class GameApp {public static void main(String[] args) {//游戏的入口new GameFrame();} }场景实物 package 框架;import 图导.Constant; import 图导.GameUtil;import java.awt.*; import java.awt.image.BufferedImage; …

【腾讯云云上实验室-向量数据库】Tencent Cloud VectorDB在实战项目中替换Milvus测试

为什么尝试使用Tencent Cloud VectorDB替换Milvus向量库&#xff1f; 亮点&#xff1a;Tencent Cloud VectorDB支持Embedding&#xff0c;免去自己搭建模型的负担&#xff08;搭建一个生产环境的模型实在耗费精力和体力&#xff09;。 腾讯云向量数据库是什么&#xff1f; 腾…