style="height:
0.011em;">
HeterogeneousMemory
style="height:
-3.322em;">Portable
style="top:
slice">
style="height:
0.011em;">
CPU/GPU/FPGA应用通过
Umpire
访问异构内存系统,并保持在不同硬件上的可移植性。
4⃣
API)
#include<umpire/ResourceManager.hpp>intmain(){auto&rm=umpire::ResourceManager::getInstance();//1GB
内存
void*gpu_ptr=rm.allocate(1024*1024*1024,"DEVICE");//512MB
主存
void*host_ptr=rm.allocate(512*1024*1024,"HOST");//释放内存
rm.deallocate(gpu_ptr);rm.deallocate(host_ptr);}- 注释:
ResourceManager提供统一接口,不用关心GPU/CPU
底层实现。
"DEVICE"、"HOST"是抽象的内存类型标识符。- 代码保持可移植性,可在不同厂商硬件上运行。
1⃣内存资源(Memory
Resources)
- 原文:Memory
Resources
system
- ****:内存资源表示系统中可用的不同类型内存。
- 解析:
- HPC
系统存在多种内存类型,如:
- HOST:CPU
主存
- DEVICE:GPU
内存
- PINNED:页锁定内存(用于加速
CPU
数据传输)
- UNIFIED/SHARED:统一虚拟内存
- HOST:CPU
- Umpire
内存资源
抽象了这些硬件差异,开发者通过统一接口访问。
- HPC
2⃣
分配器(Allocators)
- 原文:Allocators
are
Umpire
- ****:分配器是应用程序与
Umpire
交互的主要接口。
- 示例代码:
#include<umpire/ResourceManager.hpp>auto&rm=umpire::ResourceManager::getInstance();//HOST
内存分配器
autoallocator=rm.getAllocator("HOST");//使用分配器分配内存
double*data=static_cast<double*>(allocator.allocate(100*sizeof(double)));//...
//释放内存
allocator.deallocate(data);- 注释:
getAllocator("HOST")返回CPU
主存分配器
allocate/deallocate提供统一分配接口- 保持代码可移植性,无需关心具体硬件
3⃣
Strategies)
- 原文:Allocation
Strategies
algorithms
- ****:分配策略允许开发者使用更高级的内存分配算法,例如内存池、线程安全分配器等。
- 示例代码:
//QuickPool
内存池
autopool=rm.makeAllocator<umpire::strategy::QuickPool>("pool",rm.getAllocator("DEVICE"));//创建线程安全的内存池
autoth_safe_pool=rm.makeAllocator<umpire::strategy::ThreadSafeAllocator>("pool2",pool);//GPU
内存
double*data=static_cast<double*>(th_safe_pool.allocate(100*sizeof(double)));//...
//释放内存
th_safe_pool.deallocate(data);- 解析:
- QuickPool:快速分配,适合重复小块分配
- ThreadSafeAllocator:保证多线程下安全访问
- 组合策略:可以在不同策略之间嵌套,实现高效且安全的内存管理
- 公式表示:
/>Allocator
=
\text{Strategy}(\text{MemoryResource})Allocator=Strategy(MemoryResource)
分配器由策略包装底层内存资源形成。
4⃣
内存操作(Operations)
- 原文:Operations
provide
tasks
- ****:Umpire
提供统一接口来执行内存操作,例如内存拷贝、迁移、初始化。
- 示例代码:
auto&rm=umpire::ResourceManager::getInstance();autocpu_allocator=rm.getAllocator("HOST");autogpu_allocator=rm.getAllocator("DEVICE");double*src=static_cast<double*>(cpu_allocator.allocate(100*sizeof(double)));double*dest=static_cast<double*>(gpu_allocator.allocate(100*sizeof(double)));//将数据从
内存
rm.copy(dest,src);- 解析:
rm.copy(dest,自动选择最佳硬件通道src)
- 无需开发者手动处理
cudaMemcpy或memcpy - 保持统一接口、跨平台可移植
- 公式表示:
MemoryResource
\text{MemoryResource}*{dest}copy(dest,src):MemoryResource∗src→MemoryResource∗dest
5⃣
多语言支持和调试工具
- 原文:Umpire
also
etc.)
- ****:
- 多语言绑定:提供
Fortran
应用集成
- 调试工具:
- Replay:记录和回放分配行为
- Caliper
Service
:性能分析工具,用于监控内存使用和优化
- 多语言绑定:提供
- 作用:
- 支持跨语言
HPC
应用
- 提高调试效率
- 帮助分析内存性能瓶颈
- 支持跨语言
总结
- Memory
Resources
→系统中的不同内存类型
- Allocators→
提供统一分配接口
- Allocation
Strategies
→高级分配策略(内存池、线程安全)
- Operations→
内存操作统一接口(拷贝、迁移、初始化)
- 多语言
&
性能分析
- QuickPool
对随机分配(4~256KB)性能最佳,比
native
倍
- 对于大块内存(256KB),Umpire
hipMalloc
μs,效果不明显,但随着分配块增大,优势显现
详细理解
1⃣内存池策略(Memory
Strategies)
- Umpire
的优势
:- 相比设备特定的
API(如
的
hipMalloc),使用内存池分配更低开销。 - 内存池通过提前分配大块连续内存,再在内部划分小块,避免频繁调用系统
API。
- 相比设备特定的
- 常用策略:
- QuickPool:快速分配,适合小块随机分配,性能最高
- DynamicPoolList:适合动态变化的分配模式
- MixedPool:混合策略,结合多种分配模式
- 公式化理解:
alloc
T_{\text{alloc}}^{\text{QuickPool}}
\ll
T_{\text{alloc}}^{\text{hipMalloc}}
style="margin-right:
0.1389em;">T
style="height:
0.05em;">alloc
style="top:
0.05em;">QuickPool
style="height:
0.3013em;">
≪style="margin-right:
0.1389em;">T
style="height:
0.05em;">alloc
style="top:
0.05em;">hipMalloc
style="height:
0.3013em;">
在随机小块分配场景下,QuickPool
分配时间远低于原生
1)
- 场景:随机分配大小在
256
KB
- 结果:
- QuickPool优于
DynamicPoolList
加速器)
- 解释:
- QuickPool
通过预分配大块显著减少了系统调用次数
- 随机分配模式对普通
hipMalloc非常慢,而池化策略几乎无影响3⃣
与原生
2)
- 实验:
- QuickPool
HIP
(
hipMalloc/hipFree)- 结果:
- QuickPool
性能提升
倍
- 即使是小块内存分配,也能显著降低延迟
- 公式化理解:
/>Speedup
=
\frac{T_{\text{HIP}}}{T_{\text{QuickPool}}}
\approx
15Speedup=
style="height:
0.1389em;">T
style="height:
0.05em;">QuickPool
style="height:
0.2861em;">
style="top:
0.1389em;">T
style="height:
0.05em;">HIP
style="height:
0.15em;">
style="height:
0.9721em;">
≈154⃣
3)
- 实验:
- 分配大小增长到
256
DEVICE)
- 结果:
- 对
DEVICE(GPU)内存:
- 小块分配略慢(1.58~1.78
倍慢于原生
hipMalloc)- 分配块越大,开销比例越低
- 256
时,Umpire
HOST(CPU)内存:
- 小块分配开销约
290
μs(元数据收集开销)
- 随着块增大,性能与系统分配接近
- 解释:
- 内存池初始化和元数据管理带来一定开销
- 对大块分配,高效的池化策略能充分利用连续内存块,提升性能
- 公式化理解:
/>Overhead
Ratio
T_{\text{Native}}}{T_{\text{Native}}}
↑}}
0Overhead
Ratio=
style="height:
0.1389em;">T
style="height:
0.05em;">Native
style="height:
0.15em;">
style="top:
0.1389em;">T
style="height:
0.05em;">Umpire
style="height:
0.2861em;">
−style="margin-right:
0.1389em;">T
style="height:
0.05em;">Native
style="height:
0.15em;">
style="height:
0.836em;">
style="height:
-3.322em;">size
↑
style="top:
slice">
style="height:
0.011em;">
0随着分配块增大,Umpire
的开销接近零,甚至略优于原生分配
5⃣
总结
/>
方面 结果 注释 小块随机分配 QuickPool >
随机小块分配场景
大块分配 256 Umpire
10%
池化策略充分发挥优势 CPU 分配
元数据开销约 μs
可关闭元数据收集 整体趋势 分配块越大,池化优势越明显 GPU/CPU 都适用
/>
结论
- Umpire
内存池策略
显著降低应用内存分配开销
- QuickPool对随机小块
GPU
分配性能最佳
- 大块分配时,Umpire
与原生
分配有影响,可通过配置优化
Umpire’s
memory
as:
SAMRAI:
C++
库,用于自适应网格细化(AMR)应用
MARBL:
/>Additional
特性优化新的ResourceAwarePool
将PendingList更新为PendingMap,利用
std::unordered_multimap、std::optional、std::reference_wrapper优化并分析
GPU
Allocator
更新到
Fortran
API
详细理解与注释
1⃣
LLNL
生产环境代码中的应用
- SAMRAI
- C++
库,用于自适应网格细化(AMR,
Adaptive
Refinement)
- 特点:网格动态变化、内存访问模式复杂
- Umpire
的内存池可以提供快速且可预测的内存分配,提高
AMR
应用性能
- MARBL
- 多物理场模拟高能密度物理
- 内存需求大,GPU/CPU
异构环境下的高性能分配很关键
- Umpire
提供统一接口管理
内存,减少开发复杂度
注释:Umpire
内存池在
大型模拟代码中,主要作用是高性能分配与可移植性。
2⃣
Umpire
ResourceAwarePool
- 利用
C++17
的特性提升性能和表达能力:
std::unordered_multimap:高效查找Pending
分配块
std::optional:可选值管理,减少无效指针操作std::reference_wrapper:安全引用管理,避免拷贝大对象- 目标:让
ResourceAwarePool更高效、更安全,尤其在
GPU
分配器优化
- IPC(Inter-Process
Communication)分配器
:支持跨进程共享内存- 优化方向:
- 提升
GPU
可访问性
- 减少同步与开销
- 对多
GPU
内核专用分配器)
- 用途:在
GPU
内核效率
- 适合高并行、高性能计算任务
(d)
更新到
接口
- C++20:
- Concepts,
ranges,
更现代化、类型安全
- Fortran
接口优化
:
- 方便
Fortran
Umpire
- 保持多语言兼容性
(e)
API
重构
- 目标:使用更多C++
concepts
- 优点:
- 类型安全:减少错误的分配器使用
- 可读性高:API
更直观
- 可扩展性好:便于未来支持更多内存类型
3⃣
HPC
中的作用:
- 高性能内存分配(HOST/GPU)
- 可移植、统一接口
- 支持复杂异构内存系统
- 未来发展方向:
- 利用现代
C++
内核内存分配支持
- 跨语言接口改进(C++20
/>关键理解点:
- ResourceAwarePool+PendingMap:用于高性能、线程安全的动态分配管理
- Device
内核内部分配,减少数据传输
- IPC
可访问的进程间共享内存
- C++17/C++20新特性:提升
API
表达能力与安全性
感兴趣可以去看看对应的代码和文档
https://github.com/LLNL/Umpire
/>https://umpire.readthedocs.io/en/develop/sphinx/tutorial/c/pools.html


