#分布式 #运行时 #高性能计算 #PGAS #异步

lamellar-impl

Lamellar 是由 Rust 开发的 HPC 系统的异步任务运行时

10 个版本

0.6.1 2024 年 2 月 16 日
0.6.0 2023 年 12 月 1 日
0.5.0 2023 年 1 月 19 日
0.5.0-rc.12022 年 11 月 22 日
0.2.2 2020 年 9 月 5 日

#hpc 中排名第 43

Download history 3/week @ 2024-06-06 3/week @ 2024-06-13 1/week @ 2024-06-20 1/week @ 2024-06-27 7/week @ 2024-07-04 4/week @ 2024-07-18 51/week @ 2024-07-25

每月下载量 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 Message
  • Collective 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 编译和运行,则无需更改即可在 localshmem 下编译和运行。

有关使用每个 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是以下之一:localshmemrofi

创建和执行注册的激活消息

有关更多详细信息和方法,请参阅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(单进程、单系统)

  1. 直接启动可执行文件
    • cargo运行 --发布

shmem(多进程、单系统)

  1. 获取lamellar_run.sh
  2. 使用lamellar_run.sh启动您的应用程序
    • ./lamellar_run -N=2 -T=10 <appname>
      • N 要启动的PE(进程)数量(默认=1)
      • T 每PE的线程数(默认=核心数/PE数)
      • 假设<appname>可执行文件位于./target/release/<appname>

rofi(多进程、多系统)

  1. 在集群上分配计算节点
    • salloc-N2
  2. 使用集群启动器启动应用程序
    • 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”内存池的初始大小。默认为1GB
    • export LAMELLAR_MEM_SIZE=$((20*1024*1024*1024)) 20GB内存池
    • 内部,Lamellar使用RDMAable内存池来存储运行时数据结构(例如DarcsOneSidedMemoryRegion等),聚合缓冲区和消息队列。根据需要,系统会动态分配额外的内存池。这可能是一项相当昂贵的操作(因为操作是在所有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}

  1. 将Lamellar下载到${ROOT}/lamellar-runtime

cd${ROOT} &&git clone https://://github.com/pnnl/lamellar-runtime

  1. 选择要使用的层片

    • 在Cargo.toml中添加“enable-rofi”功能(或传递--features enable-rofi到您的cargo build命令),否则将仅构建对本地和shmem后端的支撑。
  2. 编译Lamellar库和测试可执行文件(可以将功能标志传递给命令行,而不是在cargo.toml中指定)

cargobuild (--release) (--featuresenable-rofi)

executables located at ./target/debug(release)/test
  1. 编译示例

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环境的各种信息
      • 额外的示例
      • 更新文档
  • 版本 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