Parsl调度框架介绍
更好的排版请阅读: https://we5lw6jk7r.feishu.cn/wiki/ZRXVwWjgLi5Mz5kJnJjcjufGnCe?from=from_copylink
Parsl
特点
实际任务黑盒化: 没有任何的任务图内部节点的优化逻辑
python+bash 描述所有逻辑
任务数据依赖需要文件系统的接入
- 在 bash_app 中,return 用于表示这个 task 需要执行的 shell 脚本具体内容,然后 bash_app 的输出结果是需要用文件进行同步的。如果是 python_app 不会遇到这个问题,return 的是 AppFuture 直接获取返回值即可。所以,在多元应用中,无法全部都用 Python 任务逻辑表达的时候,是需要依赖文件进行数据的同步的!
Executors 抽象适合将规模扩展到万节点以上规模
带着问题看
是否支持异构?
宏观角度看异构设备
- Cuda:只看到简单的应用
- OpenMP:看到在 mpi 的测试用例里面使用
- HIP:没看到
- sycl/kokkos:没看到
Executors 逻辑处理器 [类似 Legion 的 Processor]
- parsl.executors.ThreadPoolExecutor: 支持本地的多线程
- parsl.executors.HighThroughputExecutor: 该执行器使用试点作业模型实现分层调度和批处理,以在多达 4000 个节点上提供高吞吐量任务执行。
- parsl.executors.WorkQueueExecutor: 集成了 zmp 的工作队列,可以扩展到万级核心,并且可以通过动态资源调整实现任务的可靠执行。
- parsl.executors.taskvine.TaskVineExecutor:该执行器使用 TaskVine 作为执行后端。 TaskVine 可扩展到数万个核心,并积极使用计算节点上的本地存储来提供各种以性能为导向的功能,包括:智能缓存并在任务和计算节点之间共享公共大文件、任务的可靠执行、动态资源大小调整、自动 Python 环境检测和共享。这些执行器涵盖了广泛的执行要求。与其他 Parsl 组件一样,有一个标准接口 (ParslExecutor) 可以实现以添加对其他执行器的支持。
Execution providers: 与资源提供者的交互 [结合 Slurm]
- 资源提供者允许 Parsl 获得计算能力。对于超级计算机,获取资源通常需要向调度程序(例如 Slurm)请求资源。 Parsl 提供商代表您编写申请“块”(例如超级计算机节点)的请求。 Parsl 预先打包了与大多数超级计算机和一些云计算服务兼容的提供程序。
- 云、超级计算机和本地 PC 提供截然不同的访问模式。
-
主要的 Execution providers
- parsl.providers.LocalProvider:该提供程序允许您在笔记本电脑或工作站上本地运行。
- parsl.providers.CobaltProvider:此提供程序允许您通过 Cobalt 调度程序来调度资源。该提供程序已弃用,并将于 2024 年 4 月删除。
- parsl.providers.SlurmProvider:此提供程序允许您通过 Slurm 调度程序来调度资源。
- parsl.providers.CondorProvider:此提供程序允许您通过 Condor 调度程序来调度资源。
- parsl.providers.GridEngineProvider:此提供程序允许您通过 GridEngine 调度程序来调度资源。
- parsl.providers.TorqueProvider:此提供程序允许您通过 Torque 调度程序来调度资源。
- parsl.providers.AWSProvider:此提供程序允许您从 Amazon Web Services 供应和管理云节点。
- parsl.providers.GoogleCloudProvider:此提供商允许您从 Google Cloud 预配和管理云节点。
- parsl.providers.KubernetesProvider:此提供程序允许您在 Kubernetes 集群上配置和管理容器。
- parsl.providers.AdHocProvider:此提供程序允许您管理节点集合上的执行以形成临时集群。
- parsl.providers.LSFProvider:该提供程序允许您通过 IBM 的 LSF 调度程序来调度资源。
是否支持分布式? 支持
- 看代码是通过 zmq 消息队列进行一些传输
Launchers: 分布式任务发射器
- Launchers 定义了如何将工作人员分散到块中可用的所有节点上。一个常见的例子是 MPILauncher,它使用 MPI 的机制在多个计算节点上启动单个程序。与 Provider 一样,Parsl 附带了适用于大多数超级计算机和云的启动器。
-
主要的 Launcher
- parsl.launchers.SrunLauncher:基于 Srun 的启动器,适用于基于 Slurm 的系统。
- parsl.launchers.AprunLauncher:Crays 的基于 Aprun 的启动器。
- parsl.launchers.SrunMPILauncher:用于使用 Srun 启动 MPI 应用程序的启动器。
- parsl.launchers.GnuParallelLauncher:使用 GNU 并行的启动器跨节点和核心启动工作程序。
- parsl.launchers.MpiExecLauncher:使用 Mpiexec 启动。
- parsl.launchers.SimpleLauncher:启动器默认为单个工作程序启动。
- parsl.launchers.SingleNodeLauncher:此启动器在单个节点上启动 workers_per_node 计数 worker。
如何支持多元任务
- 核心:把任务包装成 @python_app ,然后发射到后端执行,非阻塞返回一个 future,最终通过 future.result()获取执行的结果
-
主要的两种抽象任务【主要的服务对象都是顶层的前端脚本语言】
- python_app
- bash_app: 用于执行 bash 脚本
-
一些神奇的内容
- parsl/tests/integration/test_channels 里包含 ssh, scp 的相关内容
如何进行任务依赖管理
Data-Flow Kernel (DFK)
如何管理数据
数据传输方案 Files: 通过文件来进行任务间数据的传递
- 缺点:效率也太低下了
import parsl
from parsl.data_provider.files import File
from parsl import bash_app
import os
parsl.load()
@bash_app
_def_ cat(_inputs_=[], _outputs_=[]):
return 'cat {} > {}'.format(" ".join([i.filepath for i in _inputs_]), _outputs_[0])
concat = cat(_inputs_=[File(os.path.join(os.getcwd(), 'hello-0.txt')),
File(os.path.join(os.getcwd(), 'hello-1.txt')),
File(os.path.join(os.getcwd(), 'hello-2.txt'))],
_outputs_=[File(os.path.join(os.getcwd(), 'all_hellos.txt'))])
# Open the concatenated file
with open(concat.outputs[0].result(), 'r') as f:
print(f.read())
DataFuture: 由 AppFuture 内部管理的返回值
- AppFutures 代表异步应用程序的执行,而 DataFutures 代表它生成的文件。 Parsl 的数据流模型(其中数据通过文件从一个应用程序流向另一个应用程序)需要这样一种构造,以使应用程序能够验证所需文件的创建,并随后在创建输入文件时解决依赖关系。调用应用程序时,Parsl 要求指定输出文件列表(使用 outputs 关键字参数)。应用程序执行时会返回每个文件的 DataFuture。在应用程序的整个执行过程中,Parsl 将监视这些文件,以 1) 确保它们已创建,2) 将它们传递给任何依赖的应用程序。
- 核心思想:在 bash_app 中,return 用于表示这个 task 需要执行的 shell 脚本具体内容,然后 bash_app 的输出结果是需要用文件进行同步的。如果是 python_app 不会遇到这个问题,return 的是 AppFuture 直接获取返回值即可。所以,在多元应用中,无法全部都用 Python 任务逻辑表达的时候,是需要依赖文件进行数据的同步的!
分布式数据传输方案 Remote Files
- Parsl 文件抽象还可以表示远程可访问的文件。在这种情况下,您可以使用文件的远程位置实例化文件对象。在执行任何依赖的应用程序之前,Parsl 会隐式地将文件暂存到执行环境。 Parsl 还会将文件的位置转换为本地文件路径,以便任何依赖的应用程序都可以像本地文件一样访问该文件。 Parsl 支持可通过 Globus、FTP 和 HTTP 访问的文件。
核心的几种并行方式
Bag of Tasks [顺序发射, 支持并行执行]
import parsl
from parsl import python_app
parsl.load()
# Map function that returns double the input integer
@python_app
_def_ app_random(_index_):
import random
# return random.random()
print(_f_"run {_index_}")
return _index_
results = []
for i in range(0, 10):
x = app_random(i)
results.append(x)
for r in results:
print(r.result())
Sequential Workflows
- 本质上就是把上一个任务返回的 future 传递给下一个任务
import parsl
from parsl import python_app, bash_app
parsl.load()
# Generate a random number
@python_app
_def_ generate(_limit_):
from random import randint
"""Generate a random integer and return it"""
return randint(1, _limit_)
# Write a message to a file
@bash_app
_def_ save(_message_, _outputs_=()):
return 'echo {} &> {}'.format(_message_, _outputs_[0])
message = generate(10)
saved = save(message, _outputs_=['output.txt'])
with open(saved.outputs[0].result(), 'r') as f:
print(f.read())
Parallel Workflows
import parsl
from parsl import python_app
parsl.load()
@python_app
_def_ wait_sleep_double(_x_, _foo_1_, _foo_2_):
import time
time.sleep(2) # Sleep for 2 seconds
return _x_*2
# Launch two apps, which will execute in parallel, since they do not have to
# wait on any futures
doubled_x = wait_sleep_double(10, None, None)
doubled_y = wait_sleep_double(10, None, None)
# The third app depends on the first two:
# doubled_x doubled_y (2 s)
# \ /
# doublex_z (2 s)
doubled_z = wait_sleep_double(10, doubled_x, doubled_y)
# doubled_z will be done in ~4s
print(doubled_z.result())
Parallel dataflow
- 可以通过在应用程序之间传递数据来开发并行数据流。在此示例中,我们创建一组文件,每个文件都有一个随机数,然后将这些文件连接成一个文件并计算该文件中所有数字的总和。对第一个应用程序的调用每个都会创建一个文件,第二个应用程序读取这些文件并创建一个新文件。最终的应用程序将总和作为 Python 整数返回。