12个版本
0.6.1 | 2024年2月16日 |
---|---|
0.6.0 | 2023年12月1日 |
0.5.0 | 2023年1月19日 |
0.5.0-rc.2 | 2022年12月23日 |
0.2.2 | 2020年9月5日 |
#96 in 异步
81 次每月下载
2MB
33K SLoC
Lamellar - Rust HPC运行时
Lamellar是研究Rust系统编程语言在HPC领域的适用性,作为C和C++的替代品,重点关注PGAS方法。
一些术语
在下面的readme和API文档(https://docs.rs/lamellar/latest/lamellar/)中,我们反复使用了一些术语,以下是对这些术语及其简要描述的说明
PE
- 处理单元,通常是多线程进程,对于那些熟悉MPI的人来说,它对应于Rank。- 通常你会在你的系统上的每个物理CPU插槽上创建1个PE,但每个CPU上可以有多个PE也是有效的
- 在某些情况下,可能会使用
Node
(表示计算节点)而不是PE
,在这种情况下,它们可以互换使用
World
- 代表你的分布式计算系统的抽象- 由N个PE组成,所有PE都可以相互通信
Team
- 世界中存在的PE的子集AM
- 简称Active MessageCollective Operation
- 通常意味着所有PE(与给定的分布式对象相关联)必须明确参与操作,否则会发生死锁。- 例如,屏障、构建新的分布式对象
One-sided Operation
- 通常意味着只有调用PE需要成功完成操作。- 例如,访问本地数据,等待本地工作完成
功能
层状结构提供了多种不同的通信模式和编程模型,以下是简要概述:
主动消息
层状结构允许在分布式环境中向远程PE发送和执行用户定义的主动消息。用户首先为他们的数据结构实现运行时导出特质(LamellarAM),然后在实现上调用一个过程宏 #[lamellar::am]。过程宏生成所有必要的代码,以启用远程执行主动消息。更多详细信息请参阅 主动消息 模块文档。
达尔斯(分布式弧)
层状结构提供了一个名为 Darc 的 Arc
的分布式扩展。达尔斯在分布式环境中提供对内部对象的安全共享访问,确保生命周期和读写访问得到正确执行。更多详细信息请参阅 Darc 模块文档。
PGAS 抽象
层状结构也通过多个接口提供 PGAS 功能。
LamellarArrays(分布式数组)
第一个是分布式数组的抽象,允许进行分布式迭代和元素的数据并行处理。更多详细信息请参阅 LamellarArray 模块文档。
低级内存区域
第二个是构建可从远程PE读取和写入的内存区域的低级(不安全)接口。请注意,除非您非常熟悉/自信于低级分布式内存(即使如此,也强烈建议您使用 LamellarArrays 接口)。更多详细信息请参阅 内存区域 模块文档。
网络后端
层状结构依赖于称为 Lamellae 的网络提供者来在整个系统中传输数据。目前存在三种这样的 Lamellae:
local
- 用于单 PE(单个系统、单个进程)开发(这是默认设置),shmem
- 用于多 PE(单个系统、多进程)开发,对于模拟分布式环境很有用(通过共享内存通信)rofi
- 用于多 PE(多系统、多进程)分布式开发,基于 Rust OpenFabrics 接口传输层(ROFI)(https://github.com/pnnl/rofi)。- 默认情况下,Rofi 的支持被禁用,因为使用它依赖于 Rofi C 库和 libfabrics 库,这些库可能没有安装在你的系统上。
- 可以通过向你的
Cargo.toml
文件中的 lamellar 条目添加features = ["enable-rofi"]
来启用它
层状结构的长期目标是,您可以使用 local
后端进行开发,然后当您准备好运行分布式时,无需更改代码即可切换到 rofi
后端。目前情况相反,如果它使用 rofi
编译和运行,则在没有更改的情况下,它将在使用 local
和 shmem
时编译和运行。
有关使用每个 Lamellae 后端的附加信息,请参阅下面的 运行层状应用程序
部分
示例
我们的仓库还提供了许多示例,突出了运行时的各种功能: 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
。
创建和执行一个注册的主动消息
请参阅主动消息文档以获取更多细节和示例
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./目标/发布/<appname>
pmi2
库用于获取分配的节点信息,并帮助设置初始握手
环境变量
Lamellar公开了一些环境变量,可以在运行时用于控制应用程序的执行
LAMELLAR_THREADS
- Lamellar PE内使用的工线程数量export LAMELLAR_THREADS=10
LAMELLAE_BACKEND
- 执行期间使用的后端。注意,如果在世界构建器中显式设置了后端,则此变量将被忽略。- 可能的值
本地
shmem
rofi
- 可能的值
LAMELLAR_MEM_SIZE
- 指定运行时 "RDMAable" 内存池的初始大小。默认为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月:添加对 "local" lamellae 的支持,为 crates.io 发布做准备 -- v0.2.1
- 2020年7月:第二个 alpha 版本 -- v0.2
- 2020年2月:第一个 alpha 版本 -- v0.1
构建要求
- Cargo.toml 中列出的 crate
可选:如果您想在分布式 HPC 环境中运行 Lamellar,则需要以下依赖项:rofi lamellae 通过在 cargo.toml 或构建时命令行中添加 "enable-rofi" 来启用。例如,使用 cargo build --features enable-rofi 构建 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
-
选择要使用的 Lamellae
- 在 Cargo.toml 中添加 "enable-rofi" 功能,如果想要使用 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
- 额外的迭代器方法
- 计数
- 求和
- reduce
- 额外的逐元素操作
- 余数
- xor
- shl, shr
- 后端操作批量改进
- 可变大小的数组索引
- GlobalLockArray的初始实现
- 'ArrayOps'特质以启用用户定义的元素类型
- 额外的迭代器方法
- AM组 - AM的运行时提供的聚合
- 通用的'AmGroup'
- 'TypedAmGroup'
- 'static'组成员
- 杂项
- 添加LAMELLLAR_DEADLOCK_TIMEOUT以帮助处理停滞的应用程序
- 在检测到恐慌和关键失败时改进错误处理和退出
- 后端线程改进
- LamellarEnv特质用于访问有关当前lamellar环境的各种信息
- 额外的示例
- 更新文档
- LamellarArrays
- 版本0.5
- 大幅改进的文档(即现在有了 ;)
- 将API“异步化” - 大多数远程操作现在返回Futures
- LamellarArrays
- 额外的单侧迭代器、局部迭代器、分布式迭代器
- 额外的逐元素操作
- 针对“调度器”的For Each
- 后端优化
- 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)
- Remote Closures功能受保护,可使用nightly rust
- 重新设计内部lamellae组织
- 对world和teams(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是Battelle运营的多项目DOE实验室。
依赖项
~10-45MB
~716K SLoC