10 个版本
0.6.1 | 2024 年 2 月 16 日 |
---|---|
0.6.0 | 2023 年 12 月 1 日 |
0.5.0 | 2023 年 1 月 19 日 |
0.5.0-rc.1 | 2022 年 11 月 22 日 |
0.2.2 | 2020 年 9 月 5 日 |
在 #hpc 中排名第 43
每月下载量 63
在 lamellar 中使用
220KB
4K SLoC
Lamellar - Rust HPC 运行时
Lamellar 是一项研究,探讨 Rust 系统编程语言在 HPC 领域的应用,作为 C 和 C++ 的替代品,重点关注 PGAS 方法。
一些术语
在整个 README 文件和 API 文档(https://docs.rs/lamellar/latest/lamellar/)中,我们反复使用了一些术语,以下是一些术语及其简要说明:
PE
- 处理单元,通常是一个多线程进程,对于熟悉 MPI 的用户,它对应于 Rank。- 通常,您会为系统上的每个物理 CPU 插槽创建一个 PE,但也可以有每个 CPU 多个 PE 的有效情况
- 在某些情况下,可能使用
Node
(意味着计算节点)而不是PE
,在这些情况下,它们是可互换的
World
- 表示您的分布式计算系统的抽象- 由 N 个 PE 组成,所有 PE 都可以相互通信
Team
- 世界中存在的 PE 的子集AM
- 短称 Active MessageCollective Operation
- 通常意味着所有 PE(与给定的分布式对象相关联)必须明确参与操作,否则将发生死锁。- 例如,屏障、构建新的分布式对象
One-sided Operation
- 通常意味着只需调用 PE 即可成功完成操作。- 例如,访问本地数据,等待本地工作完成
功能
Lamellar 为分布式应用程序提供了几种不同的通信模式和编程模型,以下简要介绍:
主动消息
层状结构允许在分布式环境中发送和执行用户定义的主动消息。用户首先为他们的数据结构实现运行时导出特征(LamellarAM),然后在实现上调用过程宏 #[lamellar::am]。该过程宏生成所有必要的代码,以启用主动消息的远程执行。更多详细信息可以在主动消息模块文档中找到。
达尔斯(分布式弧)
层状结构提供了一个名为Arc
的分布式扩展,称为Darc。达尔斯在分布式环境中提供对内部对象的安全共享访问,确保生命周期和读写访问被正确执行。更多详细信息可以在Darc模块文档中找到。
PGAS 抽象
层状结构还通过多个接口提供 PGAS 功能。
层状数组(分布式数组)
第一个是一个分布式数组的 高级抽象,允许对元素进行分布式迭代和数据并行处理。更多详细信息可以在LamellarArray模块文档中找到。
低级内存区域
第二个是一个低级(不安全)接口,用于构建可以从远程 PE 读取和写入的内存区域。请注意,除非你对低级分布式内存非常熟悉/自信(即使如此),也非常推荐你使用 LamellarArrays 接口。更多详细信息可以在内存区域模块文档中找到。
网络后端
层状结构依赖于称为 Lamellae 的网络提供商来在整个系统中传输数据。目前存在三种这样的 Lamellae:
local
- 用于单 PE(单个系统、单个进程)开发(这是默认设置),shmem
- 用于多 PE(单个系统、多进程)开发,对于模拟分布式环境很有用(通过共享内存通信)rofi
- 用于多 PE(多系统、多进程)分布式开发,基于 Rust OpenFabrics Interface 传输层(ROFI)(https://github.com/pnnl/rofi)。- 默认情况下,Rofi 的支持是禁用的,因为使用它依赖于 Rofi C 库和 libfabrics 库,这些库可能没有安装在您的系统上。
- 可以通过在您的
Cargo.toml
文件中的 lamellar 条目中添加features = ["enable-rofi"]
来启用它
层状结构的长期目标是,您可以使用 local
后端进行开发,然后在您准备好运行分布式时,无需更改代码即可切换到 rofi
后端。目前情况相反,如果它使用 rofi
编译和运行,则无需更改即可在 local
和 shmem
下编译和运行。
有关使用每个 Lamellae 后端的更多信息,请参阅下面的Running Lamellar Applications
部分
示例
我们的存储库还提供了许多示例,突出了运行时的各种功能:https://github.com/pnnl/lamellar-runtime/tree/master/examples
此外,我们正在编译一组基准测试(其中一些具有多个实现),这些基准测试可能也有助于您参考:https://github.com/pnnl/lamellar-benchmarks/
以下是一些突出显示lamellar部分功能的简单示例,更深入示例可以在各种功能的文档中找到。
选择Lamellae并构建lamellar世界实例
您可以在运行时选择要使用的后端,如下所示
use lamellar::Backend;
fn main(){
let mut world = lamellar::LamellarWorldBuilder::new()
.with_lamellae( Default::default() ) //if "enable-rofi" feature is active default is rofi, otherwise default is `Local`
//.with_lamellae( Backend::Rofi ) //explicity set the lamellae backend to rofi,
//.with_lamellae( Backend::Local ) //explicity set the lamellae backend to local
//.with_lamellae( Backend::Shmem ) //explicity set the lamellae backend to use shared memory
.build();
}
或通过设置以下环境变量: LAMELLAE_BACKEND="lamellae"
其中lamellae是以下之一:local
、shmem
或 rofi
。
创建和执行注册的激活消息
有关更多详细信息和方法,请参阅Active Messaging文档
use lamellar::active_messaging::prelude::*;
#[AmData(Debug, Clone)] // `AmData` is a macro used in place of `derive`
struct HelloWorld { //the "input data" we are sending with our active message
my_pe: usize, // "pe" is processing element == a node
}
#[lamellar::am] // at a highlevel registers this LamellarAM implemenatation with the runtime for remote execution
impl LamellarAM for HelloWorld {
async fn exec(&self) {
println!(
"Hello pe {:?} of {:?}, I'm pe {:?}",
lamellar::current_pe,
lamellar::num_pes,
self.my_pe
);
}
}
fn main(){
let mut world = lamellar::LamellarWorldBuilder::new().build();
let my_pe = world.my_pe();
let num_pes = world.num_pes();
let am = HelloWorld { my_pe: my_pe };
for pe in 0..num_pes{
world.exec_am_pe(pe,am.clone()); // explicitly launch on each PE
}
world.wait_all(); // wait for all active messages to finish
world.barrier(); // synchronize with other PEs
let request = world.exec_am_all(am.clone()); //also possible to execute on every PE with a single call
world.block_on(request); //both exec_am_all and exec_am_pe return futures that can be used to wait for completion and access any returned result
}
创建、初始化和遍历分布式数组
有关更多详细信息和方法,请参阅LamellarArray文档
use lamellar::array::prelude::*;
fn main(){
let world = lamellar::LamellarWorldBuilder::new().build();
let my_pe = world.my_pe();
let block_array = AtomicArray::<usize>::new(&world, 1000, Distribution::Block); //we also support Cyclic distribution.
block_array.dist_iter_mut().enumerate().for_each(move |(i,elem)| elem.store(i) ); //simultaneosuly initialize array accross all pes, each pe only updates its local data
block_array.wait_all();
block_array.barrier();
if my_pe == 0{
for (i,elem) in block_array.onesided_iter().into_iter().enumerate(){ //iterate through entire array on pe 0 (automatically transfering remote data)
println!("i: {} = {})",i,elem);
}
}
}
在激活消息中使用Darc
有关更多详细信息和方法,请参阅Darc文档
use lamellar::active_messaging::prelude::*;
use std::sync::atomic::{AtomicUsize,Ordering};
#[AmData(Debug, Clone)] // `AmData` is a macro used in place of `derive`
struct DarcAm { //the "input data" we are sending with our active message
cnt: Darc<AtomicUsize>, // count how many times each PE executes an active message
}
#[lamellar::am] // at a highlevel registers this LamellarAM implemenatation with the runtime for remote execution
impl LamellarAM for DarcAm {
async fn exec(&self) {
self.cnt.fetch_add(1,Ordering::SeqCst);
}
}
fn main(){
let mut world = lamellar::LamellarWorldBuilder::new().build();
let my_pe = world.my_pe();
let num_pes = world.num_pes();
let cnt = Darc::new(&world, AtomicUsize::new());
for pe in 0..num_pes{
world.exec_am_pe(pe,DarcAm{cnt: cnt.clone()}); // explicitly launch on each PE
}
world.exec_am_all(am.clone()); //also possible to execute on every PE with a single call
cnt.fetch_add(1,Ordering::SeqCst); //this is valid as well!
world.wait_all(); // wait for all active messages to finish
world.barrier(); // synchronize with other PEs
assert_eq!(cnt.load(Ordering::SeqCst),num_pes*2 + 1);
}
使用Lamellar
Lamellar能够在单节点工作站以及分布式HPC系统上运行。对于工作站,只需将以下内容复制到您的Cargo.toml文件的依赖项部分即可
lamellar= "0.6.1"
如果您计划在分布式HPC系统上使用它,请将以下内容复制到您的Cargo.toml文件中
lamellar= {version= "0.6.1",features= ["enable-rofi"]}
注意:从Lamellar 0.6.1版本开始,不再需要手动安装Libfabric,构建过程现在将尝试自动为您构建libfabric。如果此过程失败,您仍然可以通过OFI_DIR环境变量传递手动安装的libfabric。
对于这两种环境,按常规构建您的应用程序
cargobuild (--release)
运行Lamellar应用程序
有几种方式可以运行Lamellar应用程序,这主要取决于您想使用的lamellae。
local(单进程、单系统)
- 直接启动可执行文件
cargo运行 --发布
shmem(多进程、单系统)
- 获取lamellar_run.sh
- 使用
lamellar_run.sh
启动您的应用程序./lamellar_run -N=2 -T=10 <appname>
N
要启动的PE(进程)数量(默认=1)T
每PE的线程数(默认=核心数/PE数)- 假设
<appname>
可执行文件位于./target/release/<appname>
rofi(多进程、多系统)
- 在集群上分配计算节点
salloc-N2
- 使用集群启动器启动应用程序
srun-N2 -mpi=pmi2./target/发布/<appname>
pmi2
库是必需的,用于获取有关分配的节点信息并帮助设置初始握手
环境变量
Lamellar公开了一些环境变量,可以在运行时用于控制应用程序执行
LAMELLAR_THREADS
- Lamellar PE内使用的工线程数export LAMELLAR_THREADS=10
LAMELLAE_BACKEND
- 执行期间使用的后端。请注意,如果在世界构建器中明确设置了后端,则忽略此变量。- 可能的值
local
shmem
rofi
- 可能的值
LAMELLAR_MEM_SIZE
- 指定运行时“可RDMA”内存池的初始大小。默认为1GBexport LAMELLAR_MEM_SIZE=$((20*1024*1024*1024))
20GB内存池- 内部,Lamellar使用RDMAable内存池来存储运行时数据结构(例如Darcs,OneSidedMemoryRegion等),聚合缓冲区和消息队列。根据需要,系统会动态分配额外的内存池。这可能是一项相当昂贵的操作(因为操作是在所有PE上同步进行的),所以运行时将在执行结束时打印一条消息,说明分配了多少额外的池。
- 如果您发现您正在动态分配新的内存池,请尝试将
LAMELLAR_MEM_SIZE
设置为更大的值
- 如果您发现您正在动态分配新的内存池,请尝试将
- 注意:在单个系统上运行多个PE时,池的总分配内存将等于
LAMELLAR_MEM_SIZE *进程数
新闻
- 2023年2月:alpha版本发布 -- v0.6.1
- 2023年11月:alpha版本发布 -- v0.6
- 2023年1月:alpha版本发布 -- v0.5
- 2022年3月:alpha版本发布 -- v0.4
- 2021年4月:alpha版本发布 -- v0.3
- 2020年9月:添加对“本地”层片的支持,为crates.io发布做准备 -- v0.2.1
- 2020年7月:第二个alpha版本发布 -- v0.2
- 2020年2月:第一个alpha版本发布 -- v0.1
构建要求
- Cargo.toml中列出的组件
可选:如果想在分布式HPC环境中运行,Lamellar需要以下依赖项:通过在cargo.toml或构建命令行中添加“enable-rofi”来启用rofi层片。例如,使用cargo build --features enable-rofi构建Rofi,然后设置ROFI_DIR环境变量为Rofi安装目录,或者让rofi-sys crate自动构建它。
发布时,Lamellar已经与以下外部软件包进行了测试
GCC | CLANG | ROFI | OFI | IB VERBS | MPI | SLURM |
---|---|---|---|---|---|---|
7.1.0 | 8.0.1 | 0.1.0 | 1.20 | 1.13 | mvapich2/2.3a | 17.02.7 |
构建软件包
以下假设根目录为${ROOT}
- 将Lamellar下载到${ROOT}/lamellar-runtime
cd${ROOT} &&git clone https://://github.com/pnnl/lamellar-runtime
-
选择要使用的层片
- 在Cargo.toml中添加“enable-rofi”功能(或传递--features enable-rofi到您的cargo build命令),否则将仅构建对本地和shmem后端的支撑。
-
编译Lamellar库和测试可执行文件(可以将功能标志传递给命令行,而不是在cargo.toml中指定)
cargobuild (--release) (--featuresenable-rofi)
executables located at ./target/debug(release)/test
- 编译示例
cargobuild --examples(--release) (--featuresenable-rofi)
executables located at ./target/debug(release)/examples/
注意:我们执行显式构建而不是 cargo run --examples
,因为它们旨在在分布式环境中运行(见下面的测试部分)。
历史记录
- 版本 0.6.1
- 清理基于锁的数据结构的API
- N路传播屏障
- 解决AM可见性问题
- 更好的错误信息
- 更新Rofi lamellae以利用rofi v0.3
- Darcs的各种修复
- 版本 0.6
- LamellarArrays
- 额外的迭代器方法
- 计数
- 求和
- 归约
- 额外的逐元素操作
- 余数
- 异或
- 左移,右移
- 后端操作批处理改进
- 可变大小的数组索引
- GlobalLockArray的初始实现
- 'ArrayOps' trait以启用用户定义的元素类型
- 额外的迭代器方法
- AM组 - 运行时提供的AM聚合
- 泛型 'AmGroup'
- 'TypedAmGroup'
- 'static'组成员
- 杂项
- 添加LAMELLLAR_DEADLOCK_TIMEOUT以帮助处理停滞的应用程序
- 更好的错误处理和检测到恐慌和关键故障时的退出
- 后端线程改进
- LamellarEnv trait以访问有关当前lamellar环境的各种信息
- 额外的示例
- 更新文档
- LamellarArrays
- 版本 0.5
- 大幅改进文档(即现在有了 ;))
- 将API“异步化” - 大多数远程操作现在返回Futures
- LamellarArrays
- 额外的单边迭代器、本地迭代器、分布式迭代器
- 额外的逐元素操作
- “调度器”的每个“调度器”
- 后端优化
- AM任务组
- AM后端更新
- 用于跟踪的钩子
- 版本 0.4
- 分布式Arcs(Darcs:分布式原子引用计数的对象)
- LamellarArrays
- UnsafeArray、AtomicArray、LocalLockArray、ReadOnlyArray、LocalOnlyArray
- 分布式迭代
- 本地迭代
- SHMEM后端
- 动态内部RDMA内存池
- 版本 0.3.0
- 递归主动消息
- 子团队支持
- 支持自定义团队架构(Examples/team_examples/custom_team_arch.rs)
- LamellarArray的初始支持(基于分布式数组的AM集合)
- Rofi 0.2的集成
- 重写示例
- 版本 0.2.2
- 在readme中提供示例
- 版本 0.2.1
- 提供本地lamellae作为默认lamellae
- 特征保护rofi lamellae,以便lamellar可以在没有libfabrics和ROFI的系统上构建
- 添加了一个用于执行分布式DFT的示例代理应用程序
- 版本 0.2
- 新的用户界面API
- 注册Active Messages(启用稳定的Rust)
- 远程闭包功能受保护,可与nightly rust一起使用
- 重新设计内部lamellae组织
- 支持世界和团队(PE的子组)的初始支持
- 版本 0.1
- 基本的init/finit功能
- 远程闭包执行
- 基本的内存管理(堆和数据段)
- 基本的远程内存区域支持(put/get)
- ROFI Lamellae(远程闭包执行、远程内存区域)
- 套接字Lamellae(远程闭包执行、有限支持远程内存区域)
- 简单示例
说明
状态
Lamellar仍在开发中,因此并非所有预期功能都尚未实现。
联系信息
当前团队成员
Ryan Friese - [email protected]
Roberto Gioiosa - [email protected] Erdal Mutlu - [email protected]
Joseph Cottam - [email protected] Greg Roek - [email protected]
前任团队成员
Mark Raugas - [email protected]
许可证
本项目采用BSD许可证 - 详细内容请参阅LICENSE.md文件。
致谢
本工作得到了太平洋西北国家实验室(PNNL)高性能数据分析(HPDA)项目的支持,PNNL是巴特尔公司运营的多项目DOE实验室。
依赖关系
~1–1.6MB
~34K SLoC