使用 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.mzph.cn/news/164313.shtml

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

相关文章

python -opencv 中值滤波 ,均值滤波,高斯滤波实战

python -opencv 中值滤波 &#xff0c;均值滤波&#xff0c;高斯滤波实战 cv2.blur-均值滤波 cv2.medianBlur-中值滤波 cv2.GaussianBlur-高斯滤波 直接看代码吧&#xff0c;代码很简单&#xff1a; import copy import math import matplotlib.pyplot as plt import matp…

c++的更严格的类型转换要求

C有更严格的类型转换要求 C中对类型转换有严格的要求&#xff0c;需要的类型和给的类型不 一致时可能会编译报错 例如&#xff1a; C语言中 #include<stdio.h> #include<stdlib.h> //全局变量 //C语言中的函数的形参的类型可以不写&#xff0c;没有返回值可以返回&…

联发科正在改写全球高端手机芯片市场格局

全球高端手机芯片市场正在重塑。 11 月 21 日&#xff0c;联发科发布了新一代卓越 5G 生成式 AI 移动芯片天玑 8300。 这款定位于中端机档位的芯片&#xff0c;无论在技术架构还是在实际性能表现上&#xff0c;都实现了对前代旗舰芯片的赶超&#xff0c;彻底打破了业内长期存…

相机和滤镜应用程序Nevercenter CameraBag Photo mac软件特点说明

Nevercenter CameraBag Photo mac是一款相机和滤镜应用程序&#xff0c;它提供了一系列先进的滤镜、调整工具和预设&#xff0c;可以帮助用户快速地优化和编辑照片。 Nevercenter CameraBag Photo mac软件特点 1. 滤镜&#xff1a;Nevercenter CameraBag Photo提供了超过200种…

复费率电表和预付费电表有哪些区别?

随着科技的发展和能源管理的日益严格&#xff0c;电表技术也在不断更新换代。复费率电表和预付费电表作为两种主流的智能电表&#xff0c;各自具有独特的优势和应用场景。接下来&#xff0c;小编来为大家详细解析这两种电表的区别及其应用场景。 一、复费率电表 1.定义及工作原…

计算机精度导致各种误差,大数吃小数

如果 p ∗ p^* p∗是p的近似, ∣ p ∗ − p ∣ |p^*-p| ∣p∗−p∣是绝对误差, ∣ p ∗ − p ∣ / ∣ p ∣ |p^*-p|/|p| ∣p∗−p∣/∣p∣是相对误差 舍入误差,就是数据表示精度不足带来的误差 a0.1234564≈0.123456fl(a) b0.1234546≈0.123455fl(b) 在上面发生了舍入误差 f…

力扣labuladong一刷day15天K个一组翻转链表与回文链表

力扣labuladong一刷day15天K个一组翻转链表与回文链表 一、25. K 个一组翻转链表 题目链接&#xff1a;https://leetcode.cn/problems/reverse-nodes-in-k-group/ 思路&#xff1a;k个一组翻转链表&#xff0c;每k个翻转抽取出一个单独的方法reverse&#xff0c;翻转a到b&…

力扣刷题第二十九天--二叉树

前言 问问自己&#xff0c;刷题的效果真的达到了吗&#xff1f; 内容 一、翻转二叉树 226.翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 递归 func invertTree(root *TreeNode) *TreeNode {if rootnil{return root}…

Vue中的$nextTick的作用

在 Vue 中&#xff0c;当某些数据发生变化时&#xff0c;DOM 并不会立即更新。相反&#xff0c;Vue 会在下一个事件循环周期&#xff08;microtask&#xff09;中异步执行更新&#xff0c;这样可以避免频繁的 DOM 操作。然而&#xff0c;有时候我们需要在 DOM 更新后执行一些操…

2024-NeuDS-数据库题目集

一.判断题 1.在数据库中产生数据不一致的根本原因是冗余。T 解析&#xff1a;数据冗余是数据库中产生数据不一致的根本原因&#xff0c;因为当同一数据存储在多个位置时&#xff0c;如果其中一个位置的数据被修改&#xff0c;其他位置的数据就不一致了。因此&#xff0c;在数据…

11.docker的网络-docker0的理解及bridge网桥模式的介绍与实例

1.docker0的基本理解 安装完docker服务后&#xff0c;我们首先查看一下宿主机的网络配置 ifconfig我们可以看到&#xff0c;docker服务会默认在宿主机上创建一个虚拟网桥docker0&#xff0c;该网桥网络的名字称为docker0。它在内核层连通了其他物理或者虚拟网卡&#xff0c;这…

ubuntu22.04系统下载程序和依赖,并拷贝到指定路径下

脚本1 apt install aptitude apt-get -d install xxx #xxx是待下载的安装包 mv /var/cache/apt/archives/* /home/tuners/1apt install aptitude apt-get -d install xxx mv /var/cache/apt/archives/*.deb /home/tuners/1 xxx 为程序包名称 /home/tuners/1为保存程序包的…

从零开始的搭建指南:开发高效的抖音预约服务小程序

预约服务小程序提高了效率&#xff0c;节省了用户时间。下文&#xff0c;小编将与大家一同探讨如何从零开始打造预约服务小程序。 第一步&#xff1a;明确需求和目标 确定你的小程序主要服务领域是什么&#xff1f;是医疗预约、美容美发、餐厅预订还是其他行业&#xff1f;明…

Python 如何开发出RESTful Web接口,DRF框架助力灵活实现!

Django Rest Framework&#xff08;DRF&#xff09;是构建强大且灵活的Web API的优秀工具。它基于Django&#xff0c;提供了一套用于构建Web API的组件和工具&#xff0c;简化了API开发过程&#xff0c;同时保留了Django的优雅和强大。 一、Web应用模式 在开发Web应用时&…

Android组件化搭建学习

什么是组件化&#xff1f; 为什么要用组件化&#xff1f;在项目的开发过程中&#xff0c;随着开发人员的增多及功能的增加&#xff0c;如果提前没有使用合理的开发架构&#xff0c;那么代码会越来臃肿&#xff0c;功能间代码耦合也会越来越严重&#xff0c;这时候为了保证项目…

C# 忽略大小写

在 C# 中&#xff0c;你可以通过以下几种方式来忽略大小写&#xff1a; 使用 ToLower 或 ToUpper 方法将字符串转换为全小写或全大写&#xff0c;然后进行比较。使用 Compare 或 CompareOrdinal 方法&#xff0c;并传入正确的 StringComparer 实例以指示比较应该忽略大小写。使…

Android 开发Java调用Kotlin提示包不存在

在kotlin代码所在module的build.gradle设置 plugins {id org.jetbrains.kotlin.android }

Unity中Shader的Standard材质解析(一)

文章目录 前言一、在Unity中&#xff0c;按一下步骤准备1、在资源管理面板创建一个 Standard Surface Shader2、因为Standard Surface Shader有很多缺点&#xff0c;所以我们把他转化为顶点片元着色器3、整理只保留主平行光的Shader效果4、精简后的最终代码 前言 在Unity中&am…

基于Springboot+Vue选课系统

选课系统要求 (1)数据库表&#xff1a;教师信息表、学生信息表、课程表、选课表 其中&#xff0c;教师信息表、学生信息表和选课表的数据需要提前设置&#xff0c;本题主要操作课程表 (2) 技术架构&#xff1a; 后台使用springboot 前端使用vue-admin-template (3) 考试时间&…

鸿蒙(HarmonyOS)应用开发——安装DevEco Studio安装

前言 HarmonyOS华为开发的操作系统&#xff0c;旨在为多种设备提供统一的体验。它采用了分布式架构&#xff0c;可以在多个设备上同时运行&#xff0c;提供更加流畅的连接和互动。HarmonyOS的目标是提供更高的安全性、更高效、响应更快的用户体验&#xff0c;并通过跨设备功能…