7.7 Huge Page 与 Fragment 机制

发布时间:2026/9/9 5:13:51

7.7 Huge Page 与 Fragment 机制
上一篇 7-6《GPU 页表更新机制》 分析了amdgpu_vm_bo_update()→amdgpu_vm_update_range()的主干并在遍历资源更新 PTE 的环节留下一个伏笔连续的物理页可以合并为大页以降低页表占用、提升 TLB 效率。本篇即展开这一优化说明 amdgpu 如何在页表更新过程中计算 Fragment 大小、并把大块连续映射直接落在高层 PDE 上。目录1 概述2 硬件基础与两个概念3 Fragment 大小的计算4 大页在页表遍历中的落地5 PTE/PDE 标志的写入6 场景与约束7 小结1 概述1.1 为什么需要大页GPUVM 的标准页粒度是 4KB。若严格按 4KB 建立映射一段较大的连续内存会消耗大量页表项以 2MB 映射为例需要 512 个 PTE占用一整张 PTB页表 BO映射 1GB 则需要 512 张 PTB 加对应的 PDB0。这带来两方面开销页表内存占用每张 PTB 是一个 4KB 的 BO大范围映射会显著增加页表本身的 VRAM 占用。TLB 压力每个 4KB 页对应一个 TLB 表项连续访问大块内存时 TLB 命中率下降地址翻译成为瓶颈。Huge Page 与 Fragment 正是针对这两方面的优化当一段虚拟地址映射到物理连续的内存、且地址与长度满足对齐条件时硬件与驱动可以用更大的粒度描述这段映射。1.2 两个层次的优化优化作用层次核心收益实现载体Huge Page页表结构省去下层页表、缩短 walk 深度高层 PDE 打AMDGPU_PDE_PTE_FLAG当作叶子Fragment硬件 TLB一个 TLB 表项覆盖整段连续区域PTE 的AMDGPU_PTE_FRAG字段bit 7-11两者并不互斥amdgpu_vm_ptes_update()先算出一段映射可用的最大frag再据此决定「在哪一级页表落地」Huge Page以及「写入 PTE 时带上多大的 Fragment 提示」。需要先厘清一个容易混淆之处Huge Page 与 Fragment 是两个不同层次的优化由同一个frag值驱动但作用对象不同——前者减少页表层级与页表 BO 占用后者是写入 PTE 的一个提示位用于提升硬件 TLB 的缓存效率。本篇会分别说明二者再解释它们如何在amdgpu_vm_ptes_update()中统一实现。2 硬件基础与两个概念2.1 页表层级与覆盖粒度要理解大页先要明确每一级页表的单个 entry 覆盖多大地址范围。amdgpu_vm_pt_level_shift()给出每级的地址右移位数block_size默认为 9//drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.cstaticunsignedintamdgpu_vm_pt_level_shift(structamdgpu_device*adev,unsignedintlevel){switch(level){caseAMDGPU_VM_PDB3:caseAMDGPU_VM_PDB2:caseAMDGPU_VM_PDB1:caseAMDGPU_VM_PDB0:return9*(AMDGPU_VM_PDB0-level)adev-vm_manager.block_size;caseAMDGPU_VM_PTB:return0;default:return~0;}}据此单个 entry 覆盖的地址范围为4KB shift层级shift单个 entry 覆盖作为叶子时的页大小PTB04KB4KB标准页PDB092MB2MBHuge PagePDB1181GB1GBGiant PagePDB227512GB一般不作叶子Huge Page 的本质就是把一段 2MB或 1GB对齐的连续映射直接写在 PDB0或 PDB1的一个 entry 上让这个本应指向下层页表的目录项PDE转而直接描述物理内存——从而省去整张下层页表。2.2 Fragment 字段Fragment 是 PTE 中的一个字段定义在 amdgpu_vm.h//drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h#defineAMDGPU_PTE_FRAG(x)((x0x1fULL)7)它占用 PTE 的 bit 7-11。当该字段为非零值frag时页粒度从 4KB 提升到1 (12 frag)并且硬件认为该 Fragment 范围内的所有 PTE flags 一致、对应物理内存连续。其价值在于L1 TLB 可以用单个表项缓存整个 Fragment在 TLB 承压时显著提升吞吐。fragment_size默认为 9对应 2MB由amdgpu_vm_adjust_size()在初始化时设定。需要强调一个关键区别Fragment 不减少页表中的 PTE 数量只影响 TLB 的缓存效率真正减少页表项数量的是 Huge Page。二者的分界以及一个仅 Fragment 生效16KB与一个 Huge Page 生效2MB的对照走查见第 4 节结合amdgpu_vm_ptes_update()选层逻辑的分析。需要注意一处架构演进Vega10 之前Fragment 字段的上限max_frag就是fragment_sizeVega10 起Fragment 字段只作用于 L1L2 直接由页表 walker 以 small/huge/giant 页喂入因此max_frag放宽到 31。2.3 PDE-as-PTE 标志把高层 PDE 当作叶子即 Huge Page依赖另一个标志//drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h#defineAMDGPU_PDE_PTE_GFX12(1ULL63)#defineAMDGPU_PDE_PTE_FLAG(adev)\((amdgpu_ip_version((adev),GC_HWIP,0)IP_VERSION(12,0,0))?\AMDGPU_PDE_PTE_GFX12:AMDGPU_PDE_PTE)当一个 PDB 层的 entry 带上此标志硬件不再把它解释为「指向下一级页表的指针」而是当作「直接描述一大页物理内存的叶子项」。GFX12 使用 bit 63早期硬件使用AMDGPU_PDE_PTE。3 Fragment 大小的计算一段映射能用多大的 Fragment由amdgpu_vm_pte_fragment()计算//drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.cstaticvoidamdgpu_vm_pte_fragment(structamdgpu_vm_update_params*params,uint64_tstart,uint64_tend,uint64_tflags,unsignedint*frag,uint64_t*frag_end){unsignedintmax_frag;if(params-adev-asic_typeCHIP_VEGA10)max_fragparams-adev-vm_manager.fragment_size;elsemax_frag31;/* 系统内存物理不连续无法合并 */if(params-pages_addr){*frag0;*frag_endend;return;}/* 取「起始地址对齐度」与「区间长度」二者能容纳的最大 2 的幂 */*fragmin_t(unsignedint,ffs(start)-1,fls64(end-start)-1);if(*fragmax_frag){*fragmax_frag;*frag_endend~((1ULLmax_frag)-1);}else{*frag_endstart(1*frag);}}关键点对齐决定 Fragmentffs(start) - 1是起始地址能被 2 整除的最高次幂对齐度fls64(end - start) - 1是区间长度能容纳的最高次幂。取二者最小值保证 Fragment 既不越过区间、也不破坏对齐。例如start 0x200页号2MB 对齐、区间 ≥ 2MB 时frag可达 9。系统内存不合并当pages_addr非空BO 位于系统内存、物理页分散直接令frag 0。Huge Page 与 Fragment 都要求物理连续因此仅对 VRAM 等连续内存生效。frag_end本次 Fragment 的结束页号。遍历过程中每处理完一个 Fragment就以新的frag_start重新计算下一个 Fragment。4 大页在页表遍历中的落地amdgpu_vm_ptes_update()在遍历地址空间时用frag与当前层级的shift/ 父层级的parent_shift比较决定在哪一级写入//drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.cshiftamdgpu_vm_pt_level_shift(adev,cursor.level);parent_shiftamdgpu_vm_pt_level_shift(adev,cursor.level-1);if(adev-asic_typeCHIP_VEGA10(flagsAMDGPU_PTE_VALID)){/* GMC v9 之前不支持 Huge Page必须下探到 PTB */if(cursor.level!AMDGPU_VM_PTB){if(!amdgpu_vm_pt_descendant(adev,cursor))return-ENOENT;continue;}}elseif(fragshift){/* Fragment 比本级 entry 覆盖还小下降到子级再试 */if(amdgpu_vm_pt_descendant(adev,cursor))continue;}elseif(fragparent_shift){/* Fragment 比父级 entry 还大上升一级再判断 */if(!amdgpu_vm_pt_ancestor(cursor))return-EINVAL;continue;}选层逻辑可概括为「让 Fragment 恰好落在合适的层级」若frag shift当前层单个 entry 覆盖的范围比 Fragment 还大说明可以下探到更细的子级去精确描述——继续descendant。若frag parent_shiftFragment 大到连父级 entry 都容得下应上升一级用更粗的粒度描述——ancestor后重试。否则当前层级正合适就在此写入。若此时cursor.level ! AMDGPU_VM_PTB写入的就是一个 PDE-as-PTE 的 Huge Page。举例映射一段 2MB 对齐、长度 2MB 的连续 VRAM。frag 9遍历到 PDB0 时shift 9满足frag shift且frag parent_shift(18)于是在 PDB0 的单个 entry上写入叶子项覆盖整 2MB不再分配 PTB。相比 4KB 粒度需要 512 个 PTE 一张 PTBHuge Page 只用一个目录项。遍历中真正写入时flags 会带上 Fragment 提示uint64_tupd_flagsflags|AMDGPU_PTE_FRAG(frag);amdgpu_vm_pte_update_flags(params,to_amdgpu_bo_vm(pt),cursor.level,pe_start,dst,nptes,incr,upd_flags);对照仅 Fragment 生效的小规模映射把同样的选层逻辑代入一个更小的例子——映射 4 个连续、16KB 对齐的 4K 页Fragment 计算frag min(ffs(start) - 1, fls64(end - start) - 1) min(≥2, 2) 2即页粒度1 (12 2) 16KB。选层判断frag(2) shiftPTB 的shift 0不成立frag(2) parent_shift父级 PDB0 的parent_shift 9不成立。两条都不满足于是停在 PTB 层写入。写入数量nptes (upd_end - frag_start) shift 4 0 4即写入4 个 PTE每个 PTE 的 bit 7-11 都被填上frag 2提示位。对比本节开头的 2MB 例子两种机制的分界一目了然机制触发条件页表里的 entry 数收益层面Fragment连续 对齐但不足高层粒度如 16KB不变仍 4 个 PTEL1 TLB 用一个表项缓存这 4 个 PTEHuge Page连续 对齐到高层粒度≥ 2MB减少1 个 PDE-as-PTE且不分配 PTB省页表 缩短 walk TLB16KB 场景Fragment 生效页表里依旧是 4 个 PTE不减少页表项数量只是让 L1 TLB 把这 4 个当成一个连续段、用单个 TLB entry 缓存。要「只占一个页表项」必须达到某一高层页表 entry 的粒度。最细的 Huge Page 是 PDB0 的 2MB512 个连续 4K 页且 2MB 对齐此时写成一个 PDE-as-PTE连 PTB 都不分配16KB 达不到因此退化不成大页。一句话总结Fragment 省的是 TLB 表项不是页表项Huge Page 才省页表项。5 PTE/PDE 标志的写入amdgpu_vm_pte_update_flags()根据落地层级决定标志——若不在 PTB 层就把该项标记为 PDE-as-PTE//drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.cstaticvoidamdgpu_vm_pte_update_flags(structamdgpu_vm_update_params*params,structamdgpu_bo_vm*pt,unsignedintlevel,uint64_tpe,uint64_taddr,unsignedintcount,uint32_tincr,uint64_tflags){structamdgpu_device*adevparams-adev;if(level!AMDGPU_VM_PTB){/* 非叶子层却要写映射说明这是 Huge Page按 PDE-as-PTE 处理 */flags|AMDGPU_PDE_PTE_FLAG(params-adev);amdgpu_gmc_get_vm_pde(adev,level,addr,flags);}elseif(adev-asic_typeCHIP_VEGA10!(flagsAMDGPU_PTE_VALID)!(flagsAMDGPU_PTE_PRT_FLAG(params-adev))){/* PTB 层的无效项设置 fault priority 相关标志 */flags|AMDGPU_PTE_EXECUTABLE|adev-gmc.init_pte_flags;}params-vm-update_funcs-update(params,pt,pe,addr,count,incr,flags);}关键分支是level ! AMDGPU_VM_PTB一旦选层逻辑决定在 PDB0/PDB1 落地映射这里就补上AMDGPU_PDE_PTE_FLAG并调用amdgpu_gmc_get_vm_pde()把地址与标志编码成硬件 PDE 格式。最终经update_funcs-update交由 CPU 直写或 SDMA 异步写入两种后端见 7-6。6 场景与约束Huge Page / Fragment 的生效有明确前提理解这些约束有助于解释「为什么某些映射没有用上大页」物理连续仅当目标内存物理连续时才可能合并。VRAM 通常连续可用大页系统内存pages_addr非空物理页分散frag被强制为 0。地址与长度对齐Fragment 取决于start的对齐度与区间长度。若用户态分配的虚拟基址或大小未按 2MB 对齐frag会被拉低退化为更小的页。内核注释也建议用户态按 Fragment 大小对齐 VA 与分配尺寸。硬件世代GMC v9Vega10之前不支持 Huge PageAMDGPU_PTE_VALID的有效映射必须下探到 PTBVega10 起 walker 直接以 small/huge/giant 页喂入 L2。综合收益满足条件时大页既减少页表 BO 的数量与 walk 深度又通过 Fragment 提升 L1 TLB 的覆盖面对大块连续显存映射如帧缓冲、大型计算缓冲尤为明显。7 小结本篇分析了 amdgpu 页表更新中的两项连续内存优化两个层次Huge Page 作用于页表结构把 2MB/1GB 对齐的连续映射写在 PDB0/PDB1 的单个 PDE-as-PTE 上省去下层页表并缩短 walkFragment 作用于硬件 TLB通过 PTE 的AMDGPU_PTE_FRAG字段让 L1 TLB 用一个表项覆盖整段连续区域。统一实现amdgpu_vm_pte_fragment()由起始对齐度与区间长度算出最大fragamdgpu_vm_ptes_update()用frag与各级shift比较决定在哪一级落地amdgpu_vm_pte_update_flags()在非 PTB 层补上AMDGPU_PDE_PTE_FLAG完成 Huge Page 写入。生效前提物理连续、地址与长度对齐、硬件世代支持Vega10 起。系统内存因物理分散frag恒为 0。至此页表更新的「写什么、写到哪一级」已经完整。下一篇 7-8《页表更新的锁与并发模型》 将转向另一个维度这些更新在并发环境下如何与 eviction、MMU Notifier 协作而不发生死锁。

相关新闻

AI推动编程成通用技能:不会写代码也能用脚本解放双手

AI推动编程成通用技能:不会写代码也能用脚本解放双手

2026/9/9 5:13:51

1. 当财务同事开始用AI写脚本:编程正在变成通用技能 1.1 一个让我改观的小场景 上周,我帮一位在财务部门工作了十几年的朋友看脚本。她没学过任何编程语言,却在AI对话框里用自己的话描述了一遍报销表合并的流程,然后真的运行起了…

嵌入式Linux屏与安卓屏怎么选?开机时间、稳定性、成本实战对比

嵌入式Linux屏与安卓屏怎么选?开机时间、稳定性、成本实战对比

2026/9/9 5:13:51

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

i.MX6ULL Platform驱动匹配机制详解:从设备树到probe调用

i.MX6ULL Platform驱动匹配机制详解:从设备树到probe调用

2026/9/9 5:13:51

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

AI代理上下文开发生命周期(CDLC)工程化实践

AI代理上下文开发生命周期(CDLC)工程化实践

2026/9/9 7:03:56

1. 这不是又一个“提示词优化指南”,而是一套真实跑通的AI代理开发流水线你有没有过这种体验:花三天调出一个能准确解析日志、自动提取IOC的AI代理,结果两周后业务逻辑一变,整个上下文就崩了——重写提示词、重测边界、重训记忆、…

ARM开源工程解读:Cortex-M上关键词唤醒与TFLM落地实践

ARM开源工程解读:Cortex-M上关键词唤醒与TFLM落地实践

2026/9/9 7:03:56

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

CubePlex开源解读:企业级Agent平台的编排、安全与可观测性实践

CubePlex开源解读:企业级Agent平台的编排、安全与可观测性实践

2026/9/9 7:03:56

CubePlex 正式开源这件事,在 Agent 圈子里讨论度不低。做 Agent 的人应该都有同感:Demo 跑通很容易,真要放到企业生产环境里,处处是坑。模型不稳定、工具调用漏参数、权限一不留神就绕过、跑一次长任务连日志都拉不出来。CubePlex…

Pandas语法真的乱吗?一文理清核心抽象与工程实践

Pandas语法真的乱吗?一文理清核心抽象与工程实践

2026/9/9 7:03:56

“Pandas语法真的很乱吗?”——坦白讲,这个问题我至少被问过几十回,每次都有学员拿着一坨报错代码、或者被loc、iloc、ix折磨到崩溃的截图来找我。初看确实挺劝退的,同样是取值,一会儿是中括号,一会儿是点号…

CubePlex开源背后:企业级Agent平台的编排与工程化实践

CubePlex开源背后:企业级Agent平台的编排与工程化实践

2026/9/9 7:03:55

一个小小的开源公告,背后往往藏着一整条产品定位和技术取舍的脉络。CubePlex这个项目,标题上写的是“企业级 Agent 平台正式开源”,听起来像是又一个 Agent 框架出来了,但把“企业级”三个字拆开看,它解决的其实是一批…

STM32实战:旋转开关ADC采样省IO与Modbus浮点传输字节序解析

STM32实战:旋转开关ADC采样省IO与Modbus浮点传输字节序解析

2026/9/9 6:53:55

这一篇是嵌入式调试笔记的第6篇,放在一起聊两个看似不相关、实际在设备联调时经常一起蹦出来的问题:一个是4档旋转开关怎么用一个IO就完成档位采集,另一个是Modbus通信里的float数据怎么保证拆分和还原不出乱子。这两个问题我在STM32F103标准…

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

中国人民大学杨琳团队《Nature Communications》 | 全球潮汐湿地土壤有机碳时空格局与环境驱动:一项2009-2020年的全球评估

2026/9/9 1:14:29

本文首发于“生态学者”!从“湿地面积”到“土壤碳密度”:为什么需要重新认识潮汐湿地蓝碳变化?潮汐湿地位于陆地与海洋的交汇地带,包括红树林、盐沼和潮滩,是全球重要的蓝碳生态系统。其土壤能够长期储存大量有机碳&a…

adb抓包

adb抓包

2026/9/8 4:55:53

前言 本文介绍如何通过 tcpdump 在 Android 手机上抓取网络数据包,并在电脑端使用 Wireshark 进行分析。适用于需要排查 App 网络请求、分析接口调用或调试网络问题的开发与测试场景。1. 手机要有 root 权限2. 下载 tcpdump3. adb push C:\Users\zhangkuixun\Downlo…

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战

2026/9/8 22:37:26

大模型推理镜像极简瘦身:从 25GB 巨无霸到 3GB 精简镜像实战 在云原生基础设施中,容器镜像体积直接决定了服务的部署速度与弹性扩容敏捷度。对于传统的 Go / Java 微服务,镜像体积通常被严格控制在 50MB 到 200MB 以内,拉取镜像只…

扩散模型图像恢复实战:从DDPM原理到PyQt5可视化系统

扩散模型图像恢复实战:从DDPM原理到PyQt5可视化系统

2026/9/9 0:03:36

简介:面向毕业设计场景的PyQt5扩散模型图像恢复项目,提供完整Python源码与项目说明,适合图像处理、深度学习方向的高年级本科生与研究生参考。项目在模块设计上覆盖图像处理、扩散模型、参数配置、用户界面与结果评估五部分,具体涉…

开关电源环路裕量测试实战:相位裕量与增益裕量详解

开关电源环路裕量测试实战:相位裕量与增益裕量详解

2026/9/9 0:03:36

1. 项目概述:为什么环路裕量测试是电子工程师绕不开的“体检项目”“从零开始的电子工程师生活(6)——环路裕量测试”,这个标题一出来,老电源工程师可能已经下意识摸了摸示波器探头,新同事则大概率在想&…

定时插座芯片怎么选?专用定时IC与单片机MCU选型对比

定时插座芯片怎么选?专用定时IC与单片机MCU选型对比

2026/9/9 0:03:36

拆开市面上不同价位的定时插座,你会发现一个有意思的现象:有的里面躺着一颗黑色的软封装芯片,丝印都看不清;有的则是一块小小的蓝色或绿色PCB,上面赫然印着STM8或者STC的字样。同样叫"定时插座",…

远程协作的工作台整理

远程协作的工作台整理

2026/9/8 4:23:39

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/8 3:19:39

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/8 4:00:23

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…