AMD KFD的BO设计分析系列7-2:GPU GART 实现深度解析--绑定机制与性能优化

目录

  • [1. GART 绑定机制](#1. GART 绑定机制)
  • [2. GART 解绑机制](#2. GART 解绑机制)
  • [3. TLB 管理](#3. TLB 管理)
  • [4. 性能优化](#4. 性能优化)
  • [5. 调试与监控](#5. 调试与监控)

GART的基本原理请查看:AMD KFD的BO设计分析系列7-1:GPU GART 实现深度解析--基础架构与工作原理

1. GART 绑定机制

1.1 绑定流程概览

复制代码
ttm_tt_bind()
    ↓
amdgpu_ttm_backend_bind()
    ↓
amdgpu_gart_bind()              ← 本章重点
    ├─> 安全检查
    ├─> 记录映射(调试)
    ├─> amdgpu_gart_map()       ← 更新页表
    │   └─> amdgpu_gmc_set_pte_pde()  ← 硬件特定格式
    ├─> 内存屏障 mb()
    ├─> 刷新 HDP 缓存
    └─> 刷新所有 VM Hub TLB

1.2 amdgpu_gart_bind() 详解

c 复制代码
int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset,
                     int pages, struct page **pagelist,
                     dma_addr_t *dma_addr, uint64_t flags)
{
    int r, i;

    // ========== 1. 安全检查 ==========
    if (!adev->gart.ready) {
        WARN(1, "trying to bind memory to uninitialized GART!\n");
        return -EINVAL;
    }

    // ========== 2. 记录映射关系(调试) ==========
#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
    unsigned t, p;
    t = offset / AMDGPU_GPU_PAGE_SIZE;
    p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
    for (i = 0; i < pages; i++, p++)
        adev->gart.pages[p] = pagelist ? pagelist[i] : NULL;
#endif

    // ========== 3. 页表未映射则跳过 ==========
    if (!adev->gart.ptr)
        return 0;

    // ========== 4. 更新页表 ==========
    r = amdgpu_gart_map(adev, offset, pages, dma_addr, flags,
                        adev->gart.ptr);

    // ========== 5. 内存屏障 ==========
    mb();  // 确保所有 PTE 写入完成

    // ========== 6. 刷新 HDP 缓存 ==========
    amdgpu_asic_flush_hdp(adev, NULL);

    // ========== 7. 刷新所有 VM Hub 的 TLB ==========
    for (i = 0; i < adev->num_vmhubs; i++)
        amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);

    return 0;
}

1.3 参数详解

参数 类型 含义 示例
adev struct amdgpu_device * GPU 设备 -
offset uint64_t GART 起始偏移(字节) 0x80000000
pages int 要绑定的 CPU 页数 256 (1MB)
pagelist struct page ** CPU 页面指针数组 ttm->pages
dma_addr dma_addr_t * DMA 地址数组 ttm_dma->dma_address
flags uint64_t PTE 标志位 READ|WRITE|WC

offset 计算示例:

c 复制代码
// ttm_tt 绑定到 GART
struct amdgpu_ttm_tt *gtt = ...;
uint64_t offset = (u64)bo_mem->start << PAGE_SHIFT;

// bo_mem->start 是页面索引
// 左移 PAGE_SHIFT 得到字节偏移
// 例如: start=2048, PAGE_SHIFT=12 → offset=0x800000 (8MB)

1.4 amdgpu_gart_map() - 页表填充

c 复制代码
int amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset,
                    int pages, dma_addr_t *dma_addr, uint64_t flags,
                    void *dst)
{
    uint64_t page_base;
    unsigned t, i;

    // 转换为 GPU 页索引
    t = offset / AMDGPU_GPU_PAGE_SIZE;

    // 遍历每个 CPU 页
    for (i = 0; i < pages; i++) {
        page_base = dma_addr[i];  // CPU 页的 DMA 地址

        // 一个 CPU 页可能包含多个 GPU 页
        // 例如:64KB CPU 页 = 16 个 4KB GPU 页
        for (unsigned j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++) {
            // 调用硬件特定函数设置 PTE
            amdgpu_gmc_set_pte_pde(adev, dst, t,
                                   page_base, flags);
            
            page_base += AMDGPU_GPU_PAGE_SIZE;  // 下一个 GPU 页
            t++;  // 下一个 PTE 索引
        }
    }
    return 0;
}

处理 CPU 页 vs GPU 页差异:

复制代码
CPU 页 (64KB):
┌────────────────────────────────────────────────┐
│         DMA Address: 0xF0000000                │
└────────────────────────────────────────────────┘
                    ↓ 拆分为 16 个 GPU 页
GPU 页 (4KB 每个):
┌─────┬─────┬─────┬─────┬───┬─────┐
│ 0K  │ 4K  │ 8K  │ 12K │...│ 60K │
└─────┴─────┴─────┴─────┴───┴─────┘
  ↓     ↓     ↓     ↓         ↓
PTE[t] PTE[t+1] ...        PTE[t+15]

每个 PTE 存储:
PTE[t+0] = 0xF0000000 | flags
PTE[t+1] = 0xF0001000 | flags
PTE[t+2] = 0xF0002000 | flags
...
PTE[t+15] = 0xF000F000 | flags

1.5 amdgpu_gmc_set_pte_pde() - 硬件 PTE 格式

c 复制代码
void amdgpu_gmc_set_pte_pde(struct amdgpu_device *adev,
                            void *cpu_pt_addr,
                            uint32_t gpu_page_idx,
                            uint64_t addr,
                            uint64_t flags)
{
    uint64_t *pte = (uint64_t *)cpu_pt_addr;
    
    // 组合物理地址和标志位
    pte[gpu_page_idx] = (addr & AMDGPU_PTE_ADDR_MASK) | flags;
}

PTE 格式(64位):

复制代码
 63                                                    12 11        0
┌──────────────────────────────────────────────────────┬──────────┐
│         Physical Address (52 bits)                   │  Flags   │
│         页对齐 (低 12 位为 0)                          │ (12 bits)│
└──────────────────────────────────────────────────────┴──────────┘

标志位详解:

位域 名称 含义
[0] VALID PTE 有效 1
[1] READABLE 可读 1
[2] WRITEABLE 可写 1
[3] EXECUTE 可执行(某些 GPU) 0/1
[4:5] CACHE 缓存策略 00=UC, 01=WC, 10=Cached
[6] SNOOPED Cache coherent 0/1
[7] SYSTEM 系统内存 1
[8:11] FRAG Fragment size 0-9 (4KB-2GB)

标志位计算示例:

c 复制代码
uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev,
                                 struct ttm_tt *ttm,
                                 struct ttm_mem_reg *mem)
{
    uint64_t flags = 0;
    
    // 基础权限
    flags |= AMDGPU_PTE_VALID;      // 位 0
    flags |= AMDGPU_PTE_READABLE;   // 位 1
    
    if (ttm->page_flags & TTM_PAGE_FLAG_WRITE)
        flags |= AMDGPU_PTE_WRITEABLE;  // 位 2
    
    // 缓存策略
    switch (ttm->caching_state) {
    case tt_cached:
        flags |= AMDGPU_PTE_SNOOPED;  // Cache coherent
        break;
    case tt_wc:
        flags |= AMDGPU_PTE_WC;       // Write-combine
        break;
    case tt_uncached:
        // 无额外标志,默认 uncached
        break;
    }
    
    // 系统内存标记
    flags |= AMDGPU_PTE_SYSTEM;
    
    return flags;
}

实际 PTE 值示例:

复制代码
物理地址: 0x00000000F0123000
标志位:   VALID | READABLE | WRITEABLE | WC | SYSTEM
         = 0x00000007

PTE = 0x00000000F0123000 | 0x00000007
    = 0x00000000F0123007

GPU 读取此 PTE 后:
- 访问 GPU VA → 转换到物理地址 0xF0123000
- 权限: 可读可写
- 缓存: Write-combine 模式

1.6 内存屏障与缓存刷新

(1) mb() - 内存屏障
c 复制代码
mb();  // Memory Barrier

作用:

  • 确保之前的所有内存写入完成
  • 防止 CPU 乱序执行导致 PTE 未写入就刷新 TLB
  • x86 编译为 mfence 指令

必要性示例:

c 复制代码
// 错误:无内存屏障
pte[0] = addr0 | flags;
pte[1] = addr1 | flags;
// CPU 可能乱序执行,先刷新 TLB
flush_tlb();  // PTE 可能还在 CPU cache

// 正确:有内存屏障
pte[0] = addr0 | flags;
pte[1] = addr1 | flags;
mb();  // 强制完成所有写入
flush_tlb();  // PTE 已写入内存
(2) amdgpu_asic_flush_hdp() - HDP 刷新
c 复制代码
amdgpu_asic_flush_hdp(adev, NULL);

HDP (Host Data Path):

  • CPU 访问 VRAM 的数据路径
  • 有自己的写缓存
  • 需要显式刷新确保 VRAM 可见

工作流程:

复制代码
CPU 写入 PTE
    ↓
CPU Cache
    ↓ (mb() 刷新)
HDP Write Cache  ← 仍在这里
    ↓ (flush_hdp() 刷新)
VRAM (页表)
    ↓
GPU 可见

实现(硬件特定):

c 复制代码
void amdgpu_asic_flush_hdp(struct amdgpu_device *adev,
                           struct amdgpu_ring *ring)
{
    if (adev->asic_funcs->flush_hdp)
        adev->asic_funcs->flush_hdp(adev, ring);
    
    // 典型实现:
    // WREG32(mmHDP_MEM_COHERENCY_FLUSH_CNTL, 1);
    // 等待完成
}

2. GART 解绑机制

2.1 解绑流程

c 复制代码
int amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset, int pages)
{
    unsigned t, p, i;

    // ========== 1. 安全检查 ==========
    if (!adev->gart.ready)
        return -EINVAL;

    // ========== 2. 清除调试数组 ==========
#ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
    t = offset / AMDGPU_GPU_PAGE_SIZE;
    p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
    for (i = 0; i < pages; i++, p++)
        adev->gart.pages[p] = NULL;
#endif

    // ========== 3. 页表未映射则跳过 ==========
    if (!adev->gart.ptr)
        return 0;

    // ========== 4. 用 dummy page 填充 PTE ==========
    t = offset / AMDGPU_GPU_PAGE_SIZE;
    for (i = 0; i < pages; i++) {
        for (unsigned j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++) {
            amdgpu_gmc_set_pte_pde(adev, adev->gart.ptr, t,
                                   adev->dummy_page_addr,
                                   AMDGPU_PTE_VALID | AMDGPU_PTE_READABLE);
            t++;
        }
    }

    // ========== 5. 刷新机制(同绑定) ==========
    mb();
    amdgpu_asic_flush_hdp(adev, NULL);
    for (i = 0; i < adev->num_vmhubs; i++)
        amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);

    return 0;
}

2.2 为什么用 Dummy Page?

对比方案:

方案 PTE 值 GPU 访问行为 优缺点
清零 PTE 0x0 (Invalid) 页面错误/崩溃 GPU hang 风险
Dummy Page dummy_addr | VALID 读到零值 安全降级
保留旧映射 原地址 读到已释放页 内存安全问题

实际场景:

c 复制代码
// BO 解绑过程中 GPU 可能仍在访问
ttm_tt_unbind(ttm);
    ↓
amdgpu_gart_unbind();  // 用 dummy page 填充
    ↓
GPU 仍在执行着色器,访问已解绑地址
    ↓
读到 dummy page (全零) → 安全
    vs.
读到无效地址 → GPU hang/崩溃 ❌

Dummy Page 标志:

c 复制代码
flags = AMDGPU_PTE_VALID | AMDGPU_PTE_READABLE;
// 注意:
// - VALID: GPU 不会产生页面错误
// - READABLE: 允许读取(返回零)
// - 无 WRITEABLE: 写操作被忽略或产生错误(取决于硬件)

3. TLB 管理

3.1 TLB 概述

TLB (Translation Lookaside Buffer):

  • GPU 的页表缓存
  • 存储最近使用的 PTE
  • 避免每次访问都查页表

层级结构:

复制代码
GPU 内存访问
    ↓
L1 TLB (每个 CU/Shader Engine)
    ↓ Miss
L2 TLB (每个 VM Hub)
    ↓ Miss
Page Table Walk (访问 VRAM 中的页表)
    ↓
获取 PTE

3.2 VM Hub 架构

什么是 VM Hub?

  • GPU 有多个独立的虚拟内存管理单元
  • 每个引擎有自己的 Hub 和 TLB

典型配置(RDNA/CDNA):

Hub ID 名称 用途 TLB 大小
0 GFX Hub 图形引擎 256 entries
1 MM Hub 多媒体(视频编解码) 128 entries
2 VC Hub VCN (视频编码) 64 entries

为什么需要多个 Hub?

  • 并行处理:图形和视频可同时运行
  • 隔离性:不同引擎的内存访问互不干扰
  • 性能:每个 Hub 专门优化

3.3 TLB 刷新机制

c 复制代码
void amdgpu_gmc_flush_gpu_tlb(struct amdgpu_device *adev,
                              uint32_t vmid,
                              uint32_t vmhub,
                              uint32_t flush_type)
{
    // 调用硬件特定函数
    if (adev->gmc.gmc_funcs->flush_gpu_tlb)
        adev->gmc.gmc_funcs->flush_gpu_tlb(adev, vmid, vmhub, flush_type);
}

参数说明:

参数 含义 典型值
vmid 虚拟机 ID(上下文 ID) 0-15
vmhub VM Hub 索引 0=GFX, 1=MM
flush_type 刷新类型 0=全部, 1=部分

硬件实现示例(MMIO 寄存器):

c 复制代码
// Vega/RDNA 系列
static void gmc_v9_0_flush_gpu_tlb(struct amdgpu_device *adev,
                                   uint32_t vmid,
                                   uint32_t vmhub,
                                   uint32_t flush_type)
{
    // 1. 准备刷新命令
    uint32_t req = VM_INVALIDATE_REQ;
    
    // 2. 写入刷新请求寄存器
    WREG32_NO_KIQ(hub->vm_inv_eng0_req + eng, req);
    
    // 3. 等待确认
    for (i = 0; i < adev->usec_timeout; i++) {
        ack = RREG32_NO_KIQ(hub->vm_inv_eng0_ack + eng);
        if (ack & (1 << vmid))
            break;
        udelay(1);
    }
    
    if (i >= adev->usec_timeout)
        DRM_ERROR("Timeout waiting for VM flush ACK!\n");
}

3.4 TLB 刷新时机

操作 是否刷新 TLB 原因
绑定 ✅ 是 新 PTE 需要可见
解绑 ✅ 是 旧 PTE 需要失效
修改标志 ✅ 是 PTE 内容改变
只读 PTE ❌ 否 无状态变化
GPU 重置 ✅ 是 清空所有缓存

刷新开销:

复制代码
单次刷新时间:   ~10-50 微秒
影响范围:       停止所有内存访问
批量优化:       合并多次刷新 → 1 次

3.5 批量刷新优化

问题:

c 复制代码
// 绑定 1000 个页面
for (i = 0; i < 1000; i++) {
    amdgpu_gart_bind(adev, offset + i * PAGE_SIZE, 1, ...);
    // 每次都刷新 TLB → 1000 次刷新!
}

优化:

c 复制代码
// 批量绑定
amdgpu_gart_bind(adev, offset, 1000, ...);
// 只刷新一次 TLB → 性能提升 100x

实现机制:

c 复制代码
int amdgpu_gart_bind(...)
{
    // 更新所有 PTE
    for (i = 0; i < pages; i++)
        update_pte(i);
    
    mb();  // 确保所有 PTE 写入完成
    
    // 只刷新一次
    flush_tlb();  // ← 批量操作的关键
}

4. 性能优化

4.1 地址转换延迟

TLB 命中 vs 未命中:

场景 延迟 说明
L1 TLB 命中 1-2 cycles 最快
L2 TLB 命中 10-20 cycles 较快
Page Table Walk 100-200 cycles 访问 VRAM
VRAM 页表在系统内存 1000+ cycles 极慢(不应发生)

影响:

复制代码
高 TLB 命中率 (95%+):
  平均延迟 = 0.95 * 2 + 0.05 * 100 = 6.9 cycles 

低 TLB 命中率 (50%):
  平均延迟 = 0.50 * 2 + 0.50 * 100 = 51 cycles 

4.2 优化策略对比表

优化方法 实现 性能提升 适用场景 难度
大页支持 使用 2MB/1GB 页 10-100x TLB 效率 大型连续 BO
批量刷新 合并 TLB 刷新 5-10x 绑定速度 多页面绑定
预取优化 连续地址访问 2-3x 带宽利用 线性访问模式 硬件自动
TLB 预热 提前绑定热点数据 减少首次访问延迟 已知访问模式
GART 分区 分离不同用途区域 减少冲突 多引擎并行

4.3 大页支持详解

页面大小对比:

页大小 TLB Entry 覆盖 需要的 Entry 数(1GB 数据)
4KB 4KB 262,144
2MB 2MB 512
1GB 1GB 1

PTE Fragment 字段:

c 复制代码
// 在 PTE 标志中指定
flags |= AMDGPU_PTE_FRAG(frag_size);

// frag_size 编码:
// 0 = 4KB (2^12)
// 1 = 8KB (2^13)
// 2 = 16KB (2^14)
// ...
// 9 = 2MB (2^21)

启用大页的条件:

  1. CPU 页大小 >= 目标大页大小
  2. 物理地址对齐到大页边界
  3. 连续的物理内存

实现示例:

c 复制代码
// 检查是否可用 2MB 页
if (num_pages >= 512 &&  // 至少 2MB
    (dma_addr & 0x1FFFFF) == 0 &&  // 2MB 对齐
    is_contiguous(dma_addr, 512)) {  // 连续
    
    flags |= AMDGPU_PTE_FRAG(9);  // 使用 2MB 页
    // 只需 1 个 PTE 覆盖 512 个 4KB 页
}

4.4 GART 地址空间布局优化

典型布局(优化后):

复制代码
GPU 虚拟地址空间:
┌─────────────────────────────┐ 0x0000000000000000
│   保留区域 (1GB)             │
├─────────────────────────────┤ 0x0000000040000000
│   GART - 图形专用 (4GB)      │ ← GFX Hub 主要区域
│   - Staging buffers         │   TLB 命中率高
│   - 纹理溢出                 │
├─────────────────────────────┤ 0x0000000140000000
│   GART - 计算专用 (4GB)      │ ← Compute 隔离区域
│   - Userptr                 │   减少 TLB 冲突
│   - Compute 输入/输出        │
├─────────────────────────────┤ 0x0000000240000000
│   GART - 视频专用 (2GB)      │ ← MM Hub 专用
│   - DMA-BUF                 │
│   - 视频帧缓冲                │
├─────────────────────────────┤ 0x00000002C0000000
│   AGP Aperture (可选)        │
├─────────────────────────────┤ 0x0000000300000000
│   VRAM Aperture             │ ← 显存直接映射
│   (物理 VRAM 大小)           │   无 TLB 转换
└─────────────────────────────┘

分区优势:

  • 不同引擎访问不同区域 → 减少 TLB 冲突
  • 专用区域可预热 TLB
  • 便于监控和调试

4.5 性能监控

关键指标:

指标 正常值 异常值 问题
TLB 命中率 >95% <80% 访问模式分散
Page Walk 次数 <5% 访问 >20% TLB 太小或抖动
GART 利用率 <80% >95% 接近耗尽
绑定延迟 <1ms/1000页 >10ms 可能碎片化

5. 调试与监控

5.1 Debugfs 接口

bash 复制代码
# 查看 GART 信息
cat /sys/kernel/debug/dri/0/amdgpu_gtt_mm

# 输出示例:
# GART: size=16384MB, used=2048MB, free=14336MB
# num_gpu_pages: 4194304
# num_cpu_pages: 4194304
# table_size: 33554432 (32MB)

详细映射信息(CONFIG_DRM_AMDGPU_GART_DEBUGFS):

bash 复制代码
cat /sys/kernel/debug/dri/0/amdgpu_gart_pages | head -20

# 输出:
# [0] 0xF0000000 (CPU page 0x...)
# [1] 0xF0001000 (CPU page 0x...)
# [2] NULL (unmapped)

5.2 Trace Points

bash 复制代码
# 启用 GART 绑定跟踪
echo 1 > /sys/kernel/debug/tracing/events/amdgpu/amdgpu_gart_bind/enable

# 运行应用
./my_gpu_app

# 查看跟踪日志
cat /sys/kernel/debug/tracing/trace

# 示例输出:
# amdgpu_gart_bind: offset=0x80000000 pages=256 flags=0x7
# amdgpu_gart_bind: offset=0x80100000 pages=512 flags=0x7

5.3 常见问题诊断

问题 1: "GART bind failed"

原因:

  • GART 未初始化(ready=false)
  • DMA 地址无效
  • 页表未映射

诊断:

bash 复制代码
dmesg | grep -i gart

# 查找:
# - "GART: num cpu pages ..." (初始化日志)
# - "Failed to allocate GART table" (分配失败)
# - "trying to bind memory to uninitialized GART" (未就绪)
问题 2: GPU Hang (TLB 相关)

症状:

  • GPU 访问违例
  • dmesg 显示 "VM fault"

诊断:

bash 复制代码
# 查看 VM fault 日志
dmesg | grep -i "vm fault"

# 输出示例:
# [drm:amdgpu_job_timedout] *ERROR* VM fault (0x01) at page 0x80001234
# 
# 分析:
# - 地址 0x80001234 在 GART 范围内
# - 检查该地址是否已绑定
# - 可能是 TLB 未刷新或 PTE 错误

验证 TLB 刷新:

c 复制代码
// 添加调试日志
printk("Flushing TLB for vmhub %d\n", i);
amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);
printk("TLB flush completed\n");
问题 3: 性能下降

诊断步骤:

  1. 检查 TLB 命中率(需要性能计数器)

  2. 查看 GART 使用率

    bash 复制代码
    cat /sys/kernel/debug/dri/0/amdgpu_gtt_mm
    # 如果 used > 95%,可能频繁换页
  3. 监控绑定频率

    bash 复制代码
    # 统计 1 秒内的绑定次数
    perf record -e probe:amdgpu_gart_bind -a sleep 1
    perf report

5.4 调试技巧

技巧 1: 验证 PTE 内容

c 复制代码
void dump_pte(struct amdgpu_device *adev, uint64_t offset)
{
    uint64_t *pte = (uint64_t *)adev->gart.ptr;
    unsigned idx = offset / AMDGPU_GPU_PAGE_SIZE;
    
    printk("PTE[%u] = 0x%016llx\n", idx, pte[idx]);
    printk("  Physical: 0x%016llx\n", pte[idx] & ~0xFFF);
    printk("  Valid: %d\n", !!(pte[idx] & AMDGPU_PTE_VALID));
    printk("  Readable: %d\n", !!(pte[idx] & AMDGPU_PTE_READABLE));
    printk("  Writeable: %d\n", !!(pte[idx] & AMDGPU_PTE_WRITEABLE));
}

技巧 2: 强制 TLB 刷新测试

c 复制代码
// 测试 TLB 刷新是否有效
amdgpu_gart_bind(...);
// 故意不刷新 TLB
// 然后访问 → 应该失败(证明 TLB 缓存了旧值)

amdgpu_gart_bind(...);
flush_all_tlb();  // 强制刷新
// 访问 → 应该成功

技巧 3: 监控 HDP 刷新

c 复制代码
// 测试 HDP 缓存影响
write_pte(...);
// 不刷新 HDP
read_from_gpu();  // 可能读到旧 PTE

write_pte(...);
flush_hdp();
read_from_gpu();  // 应该读到新 PTE

小结

关键要点

  1. 绑定机制

    • 三层函数:bind → map → set_pte_pde
    • 必须刷新 mb、HDP、TLB
    • 处理 CPU 页 vs GPU 页差异
  2. TLB 管理

    • 多个 VM Hub,各自独立 TLB
    • 刷新开销大,需要批量优化
    • 命中率直接影响性能
  3. 性能优化

    • 大页减少 TLB 压力 10-100x
    • 批量刷新减少开销 5-10x
    • 地址空间分区减少冲突
  4. 调试方法

    • Debugfs 查看状态
    • Trace points 跟踪操作
    • 性能计数器监控 TLB
相关推荐
嵌入式郑工1 天前
RK3566 LubanCat 开发板 USB Gadget 配置完整复盘
linux·驱动开发·ubuntu
雾削木2 天前
树莓派 ESPHome 固件编译与烧录全攻略(解决超时与串口识别问题)
驱动开发
春日见3 天前
win11 分屏设置
java·开发语言·驱动开发·docker·单例模式·计算机外设
DarkAthena3 天前
【GaussDB】手动编译不同python版本的psycopg2驱动以适配airflow
驱动开发·python·gaussdb
松涛和鸣4 天前
DAY66 SPI Driver for ADXL345 Accelerometer
linux·网络·arm开发·数据库·驱动开发
嵌入式郑工4 天前
# RK3576 平台 RTC 时钟调试全过程
linux·驱动开发·ubuntu
GS8FG4 天前
针对Linux,RK3568平台下,I2C驱动的一点小小的领悟
linux·驱动开发
一路往蓝-Anbo4 天前
第 4 篇:策略模式 (Strategy) —— 算法的热插拔艺术
网络·驱动开发·stm32·嵌入式硬件·算法·系统架构·策略模式
A-花开堪折4 天前
RK3568 Android 11 驱动开发(五):串口驱动适配
驱动开发
DeeplyMind4 天前
AMD ROCm-SVM技术的实现与应用深度分析目录
svm·rocm·kfd