当前位置: 首页 > news >正文

Nvidia cuDNN 面试准备

CUDA 优化核心

CUDA 优化核心是让计算单元尽量忙起来,同时减少 memory stall。通常从 memory coalescing、shared memory reuse、减少 bank conflict、提高 occupancy、减少分支发散、合理使用 vectorized load/store 和 asynchronous copy 等方向优化。

memory stall

Memory stall指的是 CPU/GPU/加速器在执行程序时,因为等待内存数据返回而被迫停下来,不能继续执行指令的现象。

常见原因

  1. Cache miss数据不在 L1/L2/L3 cache,需要访问主存。
  2. 内存带宽不足:多个核心、线程或算子同时访问内存,带宽被打满。
  3. 访存不连续:随机访问、stride 太大、数据 layout 不合理,导致 cache 利用率差。

优化方向

  1. 提高 cache locality,比如让访问更连续;
  2. 减少重复访存,比如复用中间结果;
  3. 改数据布局,比如 NHWC / NCHW / blocking;
  4. 对 GPU,优化 shared/local memory 使用,减少 global memory 访问;

Memory coalescing

Memory coalescing指的是 GPU 里多个线程访问内存时,硬件把这些访问合并成更少、更大的连续内存事务,从而提高访存效率。

也就是说:一组线程一起读写连续地址,硬件可以一次性批量搬数据;如果地址很散,就只能拆成很多次访问,性能会差很多。

bank conflict

Bank conflict通常指 GPU 的shared memory / local memory访问冲突:多个线程同时访问同一个 memory bank,硬件没法并行服务,只能把访问拆开串行执行,导致变慢。

优化

  1. 加 padding
  2. 调整访问模式,尽量让同一个 warp 内的线程访问不同 bank

occupancy

Occupancy在 GPU profiling 里一般指:一个计算单元上实际驻留的线程/warp/wave 数量,占硬件理论最大可驻留数量的比例。

occupancy 不是越高越好。

很多高性能 kernel 可能 occupancy 只有 30%~60%,但因为每个线程做了更多计算、数据复用更好、寄存器 tile 更大,反而更快。

asynchronous copy

Asynchronous copy指的是:把数据从一块内存搬到另一块内存时,发起 copy 后不立刻等待它完成,而是让计算继续执行,等真正需要数据时再同步。

比如做矩阵乘法、卷积、attention 时,经常要先把一块 tile 从 global memory 搬到 shared memory,再用这块 tile 做计算。

核心收益是:把访存延迟藏到计算后面

什么是 epilogue fusion

epilogue fusion 一般指在 GEMM / matmul / conv 这类主计算 kernel 的“收尾阶段”直接融合后处理操作。比如:bias、activation、scale、cast、quantize 等。

这样做的好处是:避免中间结果写入 global memory 再读回来,也减少额外 kernel launch 开销

Tensor Core

Tensor Core 是专门加速矩阵乘累加的硬件单元,可以在一个指令中完成小矩阵 MMA 操作,比如 FP16 输入、FP32 accumulate。深度学习中 GEMM 和 convolution 都可以映射到 MMA,所以 Tensor Core 是 cuDNN 性能优化的核心之一。

GEMM

优化

naive matmul 每个线程计算一个 C 元素,重复从 global memory 读取 A 和 B,memory bandwidth 浪费严重。优化方式是按 tile 把 A、B 加载到 shared memory,让 block 内线程复用数据,再进一步用 register tiling、warp-level tiling 和 tensor core 提升计算效率。

small GEMM 为什么难优化

核心原因是:矩阵太小,GPU 还没来得及把算力吃满,开销和访存就已经占了大头

batched GEMM

batched GEMM 指的是一次 kernel 调用里计算一批形状相同或规则相同的 GEMM。

A: [B, M, K] B: [B, K, N] C: [B, M, N]

grouped GEMM

grouped GEMM指一次 kernel 调用里计算一组形状可能不同的 GEMM。

GEMM 0: [16, 64] @ [64, 32] GEMM 1: [32, 64] @ [64, 16] GEMM 2: [8, 128] @ [128, 64] GEMM 3: [64, 32] @ [32, 32]

grouped GEMM 的难点是:不同 GEMM 的 tile 数量不同,不能简单按 batch index 平均分配工作

更合理的是把每个 GEMM 拆成 tiles,然后全局调度

Convolution

优化

卷积本质上可以转化为矩阵乘。常见实现包括 direct convolution、im2col + GEMM、implicit GEMM、Winograd 和 FFT。实际 cuDNN 会根据 shape、数据类型、layout、hardware 选择不同 algorithm。

输出 shape 计算

Conv2D

Input: [N, C_in, H_in, W_in] Weight: [C_out, C_in/groups, K_h, K_w] Output: [N, C_out, H_out, W_out]
H_out = floor((H_in + 2 * P_h - D_h * (K_h - 1) - 1) / S_h + 1) W_out = floor((W_in + 2 * P_w - D_w * (K_w - 1) - 1) / S_w + 1)

Conv3D

Input: [N, C_in, D_in, H_in, W_in] Weight: [C_out, C_in/groups, K_d, K_h, K_w] Output: [N, C_out, D_out, H_out, W_out]
D_out = floor((D_in + 2 * P_d - Dilation_d * (K_d - 1) - 1) / S_d + 1) H_out = floor((H_in + 2 * P_h - Dilation_h * (K_h - 1) - 1) / S_h + 1) W_out = floor((W_in + 2 * P_w - Dilation_w * (K_w - 1) - 1) / S_w + 1)

groups 对输出空间 shape 的影响

groups不影响 H/W/D 的输出尺寸,只影响通道连接方式和 weight shape

C_in % groups == 0 C_out % groups == 0

implicit GEMM

implicit GEMM通常指:把卷积 Conv 看成 GEMM 来算,但不真的显式生成 im2col 矩阵

1×1 conv

1×1 conv 本质上就是对每个空间位置做一次矩阵乘法,所以优化重点通常不是卷积窗口,而是GEMM / layout / memory access / fusion

X: [N, C_in, H, W] W: [C_out, C_in, 1, 1] Y: [N, C_out, H, W]
Y[n, :, h, w] = W[:, :] × X[n, :, h, w]

更适合 NHWC

优先转成 GEMM

最常见优化是直接走高性能 GEMM

避免 im2col

可能会产生不必要的 memory copy。

Depthwise conv

Depthwise convolution是一种“每个输入通道单独做卷积”的卷积方式。

普通卷积会同时混合空间信息通道信息;Depthwise conv 只做每个通道内部的空间卷积,不做通道之间的混合。

实际模型里经常和1×1 conv搭配,这里的1×1 conv 就是为了混合不同 channel 信息

Attention

Self-Attention 复杂度

时间复杂度:O(n² · d) 空间复杂度:O(n²) n = sequence length d = hidden size

Multi-Head Attention

Multi-Head Attention就是把一次 attention 拆成多组小 attention 并行做,每一组叫一个head

单头 attention 只用一种方式看上下文;多头 attention 允许模型从多个角度同时看上下文。

好处

  1. 表达能力更强,不同 head 可以学习不同类型的 token 关系。
  2. 更适合并行,多个 head 可以并行计算,GPU 上通常会把它们 batch 到一起做矩阵乘法。

GQA 与 MQA

标准 MHA,每个 Q head 都对应自己的一组 K/V head。

MQA = 多个 Q head 共享同一组 K/V head。

GQA = 把多个 Q head 分成若干组,每组共享一组 K/V head。

FlashAttention

FlashAttention 快的核心原因:不是减少了 Self-Attention 的理论计算量,而是大幅减少了 HBM/global memory 读写。

标准 attention 里会显式生成一个很大的 attention matrix:

S = QKᵀ // [seq_len, seq_len] P = softmax(S) // [seq_len, seq_len] O = P V
把 Q / K / V 切成 block 每次只算一小块 QKᵀ 立刻做局部 softmax 统计 立刻乘 V 累加到输出 O 丢掉当前 attention block 继续处理下一块

C++

vector 与 list 区别

特性vectorlist
底层结构连续数组双向链表
随机访问O(1)O(n)
尾部插入平均 O(1)O(1)
中间插入/删除O(n)已知位置 O(1)
遍历性能通常很快通常较慢
cache locality
内存开销
iterator 稳定性较差较好

为什么 list 内存开销大

list内存开销大,主要因为它不是只存数据,而是每个元素都要单独包装成一个节点 node

unordered_mapmap区别

维度std::mapstd::unordered_map
底层结构红黑树 / 平衡二叉搜索树哈希表
元素顺序按 key 自动排序无序,遍历顺序不保证
查找复杂度O(log n)平均O(1),最坏O(n)
插入复杂度O(log n)平均O(1),最坏O(n)
删除复杂度O(log n)平均O(1),最坏O(n)
是否支持lower_bound/upper_bound支持不支持有序意义上的范围查询
是否适合范围查询适合不适合
性能稳定性稳定,基本一直是O(log n)依赖 hash 质量,冲突多会退化
内存开销每个节点有父/左/右指针、颜色位等有 bucket 数组 + 节点/链表开销
cache locality一般,树节点分散一般,也可能分散
典型用途排序、区间查询、找最近 key词频统计、缓存、ID 映射、快速查找
遍历结果key 从小到大不确定顺序
自定义 key 要求需要能比较大小,比如operator<或 comparator需要 hash 函数和相等判断,比如hash==

匿名函数

C++ 匿名函数就是lambda

[capture](parameters) -> return_type { // body };

捕获方式主要写在[]里。

不捕获的话,lambda 内部不能使用外部局部变量。

值捕获:[x]

int x = 10; auto f = [x]() { return x; }; x = 20; std::cout << f() << std::endl; // 10

[x]表示把x的值拷贝进 lambda 对象里。

注意:捕获发生在 lambda 创建时,不是在调用时。

值捕获默认不能修改

如果想修改 lambda 内部那份拷贝,要加mutable

int x = 10; auto f = [x]() mutable { x++; return x; }; std::cout << f() << std::endl; // 11 std::cout << x << std::endl; // 10,外部 x 不变

引用捕获:[&x]

int x = 10; auto f = [&x]() { return x; }; x = 20; std::cout << f() << std::endl; // 20

[&x]表示 lambda 内部引用外部的x。要注意生命周期

默认值捕获:[=]

int a = 1; int b = 2; auto f = [=]() { return a + b; };

[=]表示:lambda 里用到的外部局部变量,默认按值捕获。

默认引用捕获:[&]

int a = 1; int b = 2; auto f = [&]() { a++; b++; };

[&]表示:lambda 里用到的外部局部变量,默认按引用捕获。

this捕获

在成员函数里,lambda 如果要访问成员变量,通常会捕获this

class Foo { private: int x_ = 10; public: void run() { auto f = [this]() { return x_; }; } };
http://www.jsqmd.com/news/939189/

相关文章:

  • 徐珊新歌《六月的简历和情书》上线:用2086年的嗓音,唱哭2026年的每一个你
  • 什么是CDN?小学生也能听懂的网络加速魔法
  • Cursor 插件,才是 AI 编程的真正终局
  • 别再傻傻分不清了!用Python实战案例帮你彻底搞懂准确率、召回率和精确度
  • ripgrep 15.1.0 官方版下载(夸克网盘+百度网盘,SHA256校验)
  • 113.手写Linux刷机自动化工具|适配小米一加,自动检设备、防翻车、批量刷分区
  • 量子模拟技术解析:非简谐振荡器的VQE实现
  • 别再手动调参了!盘点10个开箱即用的相机标定工具(含OpenCV/Kalibr/Matlab保姆级对比)
  • 深度实战:Python爬取今日头条关键词搜索结果的完整指南(Ajax接口与signature签名破解)爬取今日头条关键词搜索结果o 技术点:Ajax接口、signature签名破解(进阶)
  • AnywhereVLA框架:语言驱动的机器人移动操作系统
  • AI时代下,Java程序员还要看源码吗?
  • 为什么很多硬件工程师工作10年,能力却只增长了2年?
  • Transformer模型在表格数据合成中的性能优化与实践
  • PyTorch轴承故障识别实战包:含CWRU一维振动数据、LSTM/1D-CNN模型权重与训练可视化图表
  • 3分钟掌握Topit:macOS窗口置顶的终极解决方案
  • LinkSwift:八大网盘直链解析神器,告别限速烦恼
  • 从SVD到RANSAC:点云平面拟合的数学原理与Python代码逐行解析(避坑参数设置)
  • 基于I2C与Arduino的模块化街机按钮控制器设计与实现
  • defer性能陷阱:我是如何解决内存逃逸问题的
  • 在线 UML 制图神器:用例图、时序图、流程图一键生成非常好用
  • WzComparerR2 终极指南:冒险岛WZ文件提取器的完整使用教程
  • 终极B站广告跳过工具:小电视空降助手完整使用指南
  • Translumo:Windows平台实时屏幕翻译工具完全指南
  • 06-02 · LLM 最新论文速览
  • 如何构建面向企业研发协作的规范化设计走查表与设计还原度优化设计系统与视觉资产库流程
  • 有哪些真正好用且不贵的 AI 写作软件?100 小时深度体验后我来交作业了
  • 5分钟搞定RabbitMQ!Docker一键安装 + 核心概念图解
  • 全国哪家台球厅设计公司的口碑较好? - myqiye
  • 现在Java面试背八股文已经没用了吗?
  • AI招聘模块接入HR系统失败率高达68%?——从API协议、数据血缘到权限治理的全链路诊断