趋近智
现代机器学习工作负载,特别是部署在异构硬件上的工作负载,通常涉及一系列复杂的操作,包括数据传输、CPU上的预处理/后处理,以及GPU或专用NPU等加速器上的密集计算。顺序执行这些操作可能导致硬件资源大量未充分利用,因为一个组件在另一个组件完成任务时常常处于闲置等待状态。异步执行和精巧的调度是运行时不可或缺的功能,它们能有效减少这些低效情况并提升吞吐量。
其主要思想是将机器学习模型推理或训练步骤表示为任务的有向无环图(DAG),节点代表操作(例如,内核启动、内存复制、同步事件),边代表依赖关系。运行时的职责是尽可能高效地执行此图,在遵守依赖关系的同时,抓住并行和重叠的机会。
运行时管理的每个工作单元都封装为一个任务。一个任务可能对应于:
cudaLaunchKernel、SYCL 内核提交)。cudaMemcpyAsync、sycl::queue::memcpy)。依赖关系决定执行顺序。例如,GPU 内核在其输入数据复制到 GPU 内存之前无法执行,而主机到设备的数据复制在其源数据在主机上准备就绪之前无法开始。这些依赖关系构成了任务 DAG 的结构。
一个简化的任务图,显示了依赖关系。内核 1 和 2 在各自数据传输完成后,可能在不同的流上并行运行。内核 3 依赖于内核 1 和内核 2 的完成,可能通过事件进行同步。
硬件加速 API 提供了异步执行的原语。在 CUDA 中,这些是流,而在 SYCL/OpenCL 中,它们是队列。入队到同一流/队列的操作通常由设备顺序执行,但不同流/队列上的操作可以并行执行,这取决于硬件资源可用性和明确的依赖关系。
任务图定义后,运行时调度器会决定实际的执行顺序和时机。常见的策略包括:
调度器的目标通常是通过使计算单元保持忙碌,并尽可能使数据传输与计算重叠来最大化资源利用率。
通过流/队列的异步操作是实现重叠的主要方式。考虑一个典型的模式:复制输入数据(主机到设备,H2D)、在设备上计算、将结果复制回(设备到主机,D2H)。
通过使用多个流和适当的事件同步来流水线化数据块的处理,运行时可以大幅度地将数据传输的延迟隐藏在计算背后。
执行时间线的比较。在同步执行中,操作顺序发生。在异步执行中,下一/前一块的数据传输(H2D,D2H)可以与当前块的 GPU 计算重叠,从而减少总执行时间。
异步性虽具强大能力,但有时仍需明确的同步。例如,CPU 可能需要等待 GPU 结果才能继续后续逻辑,或者在最终归约步骤之前,来自多个并行流的结果必须就绪。
运行时 API 提供了同步原语:
cudaStreamSynchronize(stream) / queue.wait():阻塞调用 CPU 线程,直到指定流/队列上所有先前提交的操作完成。cudaEventSynchronize(event) / event.wait():阻塞调用 CPU 线程,直到指定事件被记录(即,相关任务完成)。cudaStreamWaitEvent(stream, event) / queue.ext_oneapi_submit_barrier(event_list):在流/队列上入队一个等待操作;该流/队列上的后续操作直到事件被记录后才会开始。这不会阻塞主机线程。过多或放置不当的同步点会抵消异步执行的优势,重新引入序列化点并降低并行性。运行时设计者必须谨慎管理同步,以确保正确性而不牺牲性能。最小化主机-设备同步点通常是一个重要的优化目标。
设计有效的异步调度器涉及多项挑战:
熟练掌握异步执行和调度对于构建能够充分发挥现代硬件能力的高性能机器学习运行时不可或缺。它需要深刻理解目标硬件的并发机制,谨慎管理依赖关系,以及巧妙的调度策略来有效重叠操作。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造