小厂 AI Infra 实习 一面
Q: 内存分级(CPU/GPU内存层次结构)?
CPU和GPU内存层次对比:
1 | CPU内存层次: GPU内存层次: |
关键差异的设计哲学:
| 维度 | CPU | GPU |
|---|---|---|
| 设计目标 | 减少延迟(大cache) | 隐藏延迟(多线程) |
| Cache比例 | 占芯片面积>50% | 占芯片面积<20% |
| 程序员参与 | 硬件透明管理 | 部分需显式管理(Shared Mem) |
| 带宽 | 中等(DDR ~50GB/s) | 极高(HBM ~2TB/s) |
| 延迟隐藏 | 靠Cache + 乱序执行 + 推测执行 | 靠大量warp切换(Latency Hiding) |
编程优化启示:
- CPU:优化Cache命中率(数据局部性、预取)
- GPU:优化带宽利用率(合并访问、向量化)和占用率(Occupancy)
- 两者共同:减少主存访问次数(数据复用)
Q: GPU内存层次结构?
NVIDIA GPU完整内存层次(以A100为例):
| 内存类型 | 容量 | 带宽 | 延迟 | 作用域 | 管理方式 | 典型用途 |
|---|---|---|---|---|---|---|
| 寄存器 | 256KB/SM | ~19TB/s | 1 cycle | 线程私有 | 编译器 | 局部变量、中间结果 |
| Shared Memory | 0-164KB/SM | ~19TB/s | ~28 cycles | Block共享 | 程序员__shared__ | Tile缓存、Reduce |
| L1 Cache | 与Shared共享192KB | ~19TB/s | ~28 cycles | Block内 | 硬件自动 | 自动缓存全局访问 |
| L2 Cache | 40MB | ~5TB/s | ~200 cycles | 全GPU | 硬件自动 | 全局内存cache |
| 全局内存(HBM) | 80GB | 2039GB/s | ~600 cycles | 全GPU | cudaMalloc | 模型权重、数据 |
| 常量内存 | 64KB | 很高(命中) | ~4 cycles(命中) | 全GPU只读 | constant | 超参数、查找表 |
| Local Memory | 溢出到HBM | 同全局 | 同全局 | 线程私有 | 编译器溢出 | 寄存器溢出数据 |
关键优化策略:
1 | // 策略1: 数据从HBM → Shared Memory → 寄存器 |
Q: 影响GPU性能的硬件因素?
GPU性能的三角模型:
1 | Compute |
各因素的详细参数和影响:
| 因素 | A100指标 | H100指标 | 如何成为瓶颈 |
|---|---|---|---|
| FP16算力 | 312 TFLOPS | 989 TFLOPS | 大矩阵乘(高算术强度) |
| INT8算力 | 624 TOPS | 1979 TOPS | 量化推理 |
| HBM带宽 | 2039 GB/s | 3350 GB/s | 小batch推理(低算术强度) |
| NVLink带宽 | 600 GB/s | 900 GB/s | 多卡通信(TP AllReduce) |
| SM数量 | 108 | 132 | 决定最大并行block数 |
| L2 Cache | 40 MB | 50 MB | 工作集超过L2时性能下降 |
| 共享内存/SM | 192 KB | 228 KB | 限制每block可用的tile大小 |
| 寄存器/SM | 65536×32bit | 65536×32bit | 限制occupancy |
实际场景中的瓶颈分析:
| 场景 | 主要瓶颈 | 原因 | 优化方向 |
|---|---|---|---|
| LLM Decode(batch=1) | HBM带宽 | 每token读全部权重 | 量化减少权重大小 |
| LLM Prefill(长序列) | Compute | 大矩阵乘 | FP8/INT8加速 |
| Reduce/LayerNorm | HBM带宽 | 算术强度低 | 算子融合 |
| 多卡训练 | 互联带宽 | AllReduce通信 | 梯度压缩/overlap |
| 小kernel连续调用 | CPU launch延迟 | 每次launch~5us | CUDA Graph |
Roofline分析实例:
1 | A100 FP16: |
Q: 递归的优缺点?
递归的本质:
1 | // 递归 = 函数调用自身 + 每次调用有栈帧开销 |
优缺点对比:
| 优点 | 缺点 |
|---|---|
| 代码简洁自然(分治/树/回溯) | 函数调用开销(~5-10ns/次) |
| 天然表达数学归纳/递推关系 | 栈溢出风险(默认栈~8MB, 深度>10万可能溢出) |
| 容易证明正确性 | 重复计算(朴素递归Fibonacci: O(2^n)) |
| 回溯/剪枝自然实现 | 调试较困难(深层栈追踪) |
优化方法:
1 | // 1. 记忆化(Memoization) — 消除重复计算 |
Q: DFS和BFS在解方程组中的区别?
图算法在稀疏线性代数中的应用:
稀疏矩阵可以看作图:非零元素A[i][j]表示节点i和j之间有边。
DFS的应用:
| 应用 | 算法 | 作用 |
|---|---|---|
| 强连通分量 | Tarjan/Kosaraju | 将大方程组分解为独立子系统 |
| 拓扑排序 | DFS后序反转 | 确定求解顺序(无环时) |
| 填入分析 | Elimination Tree | 预测LU分解的非零模式 |
| 矩阵重排序 | 嵌套剖分(Nested Dissection) | 减少填入元素 |
BFS的应用:
| 应用 | 算法 | 作用 |
|---|---|---|
| 带宽最小化 | Cuthill-McKee(RCM) | 减少矩阵带宽→减少填入 |
| 层次划分 | Level Set | 确定并行度(同层可并行) |
| 图分区 | BFS-based partitioning | 多处理器负载均衡 |
| 最短路径 | BFS(无权) | 确定通信距离 |
在数值计算中的具体区别:
1 | 稀疏方程组 Ax = b: |
Q: C++静态变量的特点?
三种static作用域的详解:
1 | // 1. 函数内static(局部static) — 单例/计数器 |
static变量的存储和初始化:
| 特性 | 全局/文件static | 局部static | 类static |
|---|---|---|---|
| 存储位置 | .data(有初值) / .bss(无初值) | 同左 | 同左 |
| 初始化时机 | main之前(动态初始化) | 首次执行到(延迟初始化) | 显式定义处 |
| 线程安全 | 保证(C++11) | 保证(C++11 Magic Static) | 需自行保证 |
| 析构时机 | main之后(反序析构) | main之后 | main之后 |
Q: 堆和栈的区别?
| 维度 | 栈(Stack) | 堆(Heap) |
|---|---|---|
| 管理 | 编译器自动(RAII) | 程序员手动(new/delete) |
| 分配速度 | 极快(~1ns, sub rsp指令) | 慢(~50-200ns, 搜索空闲块) |
| 释放速度 | 极快(add rsp) | 中等(归还到空闲链表) |
| 大小 | 固定(通常2-8MB) | 灵活(受虚拟地址空间限制) |
| 方向 | 向低地址增长 | 向高地址增长 |
| 碎片 | 无(LIFO天然连续) | 有(频繁malloc/free后碎片化) |
| Cache | 极好(活跃数据连续) | 较差(分散分配) |
| 线程 | 每线程独立栈 | 进程内共享(需同步) |
| 溢出 | Stack Overflow(段错误) | 返回NULL/抛bad_alloc |
| 适用 | 小对象、生命周期确定 | 大对象、生命周期不确定 |
1 | // 性能对比示例: |
Q: 图算法了解哪些?
图算法分类及复杂度:
| 类别 | 算法 | 时间复杂度 | 适用条件 |
|---|---|---|---|
| 遍历 | BFS | O(V+E) | 无权最短路/层次遍历 |
| DFS | O(V+E) | 连通性/拓扑/回溯 | |
| 最短路径 | Dijkstra | O((V+E)logV) | 非负权单源 |
| Bellman-Ford | O(VE) | 有负权/检测负环 | |
| Floyd-Warshall | O(V³) | 全源最短路 | |
| A* | 启发式 | 带启发函数的单目标 | |
| 最小生成树 | Prim | O(ElogV) | 稠密图 |
| Kruskal | O(ElogE) | 稀疏图(配合并查集) | |
| 拓扑排序 | Kahn(BFS) | O(V+E) | DAG/编译依赖 |
| DFS后序 | O(V+E) | 同上 | |
| 强连通分量 | Tarjan | O(V+E) | 有向图分解 |
| Kosaraju | O(V+E) | 两次DFS | |
| 匹配 | 匈牙利 | O(V³) | 二分图最大匹配 |
| 网络流 | Dinic | O(V²E) | 最大流/最小割 |
| 连通性 | 并查集 | 近O(1)/操作 | 动态连通性 |
在HPC/AI中的图算法应用:
- 拓扑排序:计算图调度(TVM/TensorRT中算子执行顺序)
- 图分区:分布式训练中的模型切分
- BFS/层次划分:稀疏矩阵并行化分析
- 最短路径:网络拓扑感知的通信路由
- 匹配:目标跟踪中的检测-轨迹关联(匈牙利算法)
Q: 手撕(思路题):CUDA实现100万个浮点数相加?
(编程题)