写点什么

OpenCL 任务调度基础介绍 | 京东物流技术团队

  • 2023-11-17
    北京
  • 本文字数:4120 字

    阅读完需:约 14 分钟

OpenCL任务调度基础介绍 | 京东物流技术团队

当前,科学计算需求急剧增加,基于 CPU-GPU 异构系统的异构计算在科学计算领域得到了广泛应用,OpenCL 由于其跨平台特性在异构计算领域渐为流行,其调度困难的问题也随之暴露,传统的 OpenCL 任务调度需要在编码阶段确定调度方案,这种人工调度难度高、适应性差、效率低下、且存在资源竞争问题。MultiCL 通过扩展 OpenCL 标准使得命令队列和设备解耦,实现了自适应调度,并为不同程度的开发人员提供了不同的调度方法,缓解了 OpenCL 的调度难题。

1 OpenCL 基本介绍

OpenCL 是第一个面向异构系统通用目的并行编程的开放式、免费标准,适用于跨 CPU、GPU 和其他处理器的异构混合编程。OpenCL 通过创建一个高效的、底层的编程接口,实现了独立于硬件、操作系统和应用程序的并行计算生态系统的基础层。OpenCL 用于协调主机和支持 OpenCL 标准的异构计算设备间的并行计算,并且具有明确的跨平台编程语言。


OpenCL 是在异构系统上进行编程的行业标准。OpenCL 不仅仅是一种编程语言,更是用于异构系统编程的行业标准框架。相较于 CUDA,OpenCL 程序可在不同供应商的硬件上移植,具有良好的功能可移植性。但是由于 OpenCL 作为一个底层通用框架,为程序员开放了大量硬件细节,这样的好处是编程人员提供了大量的程序优化空间,但是也造成了 OpenCL 程序不具备良好的性能可移植性,即相同程序在移植过程中会造成极大的性能差异。同时对于程序员而言,任务的划分以及调度完全由编程人员指定,编程人员难以感知和预测整体系统的运行状态和负载情况,使得程序难以达到最优效率。


OpenCL 架构为了实现跨平台的异构计算,包含以下部分:


  1. OpenCL 平台层:OpenCL 平台层允许主机程序发现 OpenCL 设备并调用该设备参与计算,并为特定设备或设备组创建上下文。

  2. OpenCL 运行时:OpenCL 运行时允许主机程序在上下文创建后对其进行操作,OpenCL 运行时为 OpenCL 程序提供了软件支持。

  3. OpenCL 编译器:OpenCL 编译器根据内核和设备扩展信息将内核文件编译为可执行文件。编译器支持基于并行扩展的 ISO C99(ISO/IEC 9899:1999-Programming languages)语言的子集。OpenCL 编译器可根据即时编译和二进制加载两种方式创建可执行文件。


OpenCL 架构可分为平台模型、执行模型、存储模型和编程模型。

2 MultiCL

OpenCL 标准要求开发人员在程序编码阶段就确定 OpenCL 程序的调度方案,使 OpenCL 面临调度难度高、适应性差、资源竞争、效率低下等问题。为了解决上述问题 Ashwin M 等人在 SunCL 基础上进一步开发的 OpenCL 任务级并行调度框架,该框架提供了跨厂商的 OpenCL 运行时支持。MultiCL 实现了上下文级别的全队列调度和针对特定命令队列的本地调度功能,作者以此为基础实现了 OpenCL 任务的动态调度。


OpenCL 内核调度策略取决于编程模型,开发者必须事先明确程序中所包含的内核调度的方案,为了实现 OpenCL 的自适应任务调度,Ashwin M 等人对 OpenCL API 进行了扩展。表 1 描述了 MultiCL 的扩展属性。


表 1 MultiCL 扩展属性



为了表达全局队列调度机制,MultiCL 框架扩展了一个新的上下文属性 CL_CONTEXT_SCHEDULER,该扩展属性实现了上下文级别的 OpenCL 自适应调度,采用 CL_CONTEXT_SCHEDULER 创建的上下文,该上下文关联的命令队列与 OpenCL 设备解耦合,即创建 OpenCL 命令队列是不用指定 OpenCL 设备,从上下文层面解决了 OpenCL 内核调度需要在编码阶段完成的问题。OpenCL 上下文属性包含两个参数,分别对应了两种全局调度策略:循环调度(ROUND_ROBIN)和自适应调度(AUTO_FIT)。循环调度是在触发调度程序时,将命令队列调度到下一个可用设备上,在实际运行过程中,由于 GPU 设备支持多内核同时执行,该方案调度时选择计算资源充足的设备进行调度,这种方法会有最小的调度开销,但是并不总是选择最佳的队列-设备映射。自动调度策略是 MultiCL 框架提出的基于内核预执行的动态调度策略,MultiCL 框架将根据动态调度策略选择最佳的队列-设备映射。


同时为了实现本地调度策略,对 OpenCL API 命令调度队列属性进行了扩展,添加了 SCHED_AUTO__或 SCHED_OFF,分别确定特定的队列是选择自动调度模式还是手动调度模式。MultiCL 框架给用户提供了选项,用户可以选用 SCHED_OFF 标志手动指定调度方案,用户也可以在创建命令队列时添加 SCHED_AUTO__标志来实现自动调度,其中包括静态调度(SCHED_AUTO_STATIC)和动态调度(SCHED_AUTO_DYNAMIC)。针对命令队列属性的修改旨在作为调度的微调参数,针对不同程度的 OpenCL 开发人员,制定了不同程度的调度策略。对于具有丰富经验的开发人员,可以直接忽略 MultiCL 框架提供的命令队列参数,选择手动调度所有队列,便于开发人员根据异构系统底层特点实现任务划分和内核优化,对于中等级别的开发人员,对底层架构有一定了解,可根据 OpenCL 内核的负载类型(例如:SCHED_IO_BOUND, SCHED_COMPUTE_BOUND 等)选择命令队列属性作为系统的调度提示,便于 MultiCL 框架根据 OpenCL 内核类型提示构建微型内核和进行调度,对于初级开发人员,则可以采用 MultiCL 框架提供的自适应动态调度,由 MultiCL 运行时决定内核调度方案,该方式会带来一定的运行时开销。


MultiCL 扩展的 clSetCommandQueueSchedProperty 命令用于标记调度程序区域,并可以在需要时设置更多调度程序标志,例如使用以下属性:SCHED_COMPUTE_BOUND,SCHED_MEM_BOUND,SCHED_IO_BOUND 或 SCHED_ITERATIVE 指定队列中的预期计算类型,与 clCreateCommandQueue 的扩展参数相对应,该函数便于调度方案优化。OpenCL 内核启动命令的参数包括命令队列,内核对象和内核的启动配置。MultiCL 为了自动调度的方便,实现了新的 OpenCL API 命令 clSetKernelWorkGroupInfo,用于设定内核配置参数,MultiCL 框架在 OpenCL 内核调度命令加入命令队列前就可以获取内核的工作组和工作组中工作项信息,便于 MultiCL 框架进行内核调度。


MultiCL 运行时设计如图 1 所示。


使用 SCHED_OFF 标志创建的用户命令队列将静态映射到所选设备(由开发人员在编码阶段指定映射方案),而使用 SCHED_AUTO 标志创建的命令队列将由 MultiCL 自动调度。MultiCL 框架将 OpenCL 内核启动命令加入队列后,如果该命令队列是 SCHED_OFF 标志创建的命令队列,则采用静态调度方案,MultiCL 运行时保留了 SunCL 运行时静态调度方案的部分,给开发人员保留了静态硬编码调度的功能。如果该命令队列是以 SCHED_AUTO 标志创建的,则交由自适应调度器映射到设备池中某一设备。上述调度过程包含了设备静态分析(在 clGetPlatformIds 读取设备信息阶段进行),内核静态分析(clBuildProgram,clCreateProgramWithSource 构建程序阶段进行),内核动态分析(clEnqueue*命令阶段)和设备映射。MultiCL 动态调度模块通过独立线程与 OpenCL 程序执行并发执行,利用 OpenCL 初始化阶段开销隐藏 MultiCL 运行时内核分析和设备分析带来的开销。



图 1 MultiCL 运行时结构图


MultiCL 动态调度包含三个主要的运行时模块:设备分析器、内核分析器和任务调度器。


  1. 设备分析器,用于收集和分析设备的性能(内存,计算能力和 I/O);

  2. 内核分析器,分析和预测内核在不同设备上的执行时间;

  3. 任务调度器,将 SCHED_AUTO 标记的命令队列中的任务调度到设备上。上述三个运行时模块在 MultiCL 对 OpenCL 标准扩展的基础上实现了 OpenCL 任务的动态调度。设备分析器和内核分析器分别对 OpenCL 设备和内核进行分析,判断 OpenCL 设备与内核的契合程度,为任务调度器提供数据基础。


设备分析器是在 OpenCL clGetPlatformIds 命令调用期间执行的设备概要分析器。设备分析器从设备概要文件中检索 OpenCL 设备的概要信息。如果文件中不存在该设备的概要信息,则 MultiCL 运行时将运行数据带宽和指令吞吐量基准测试,并将测得的指标作为静态信息存入设备概要文件中。基准测试源于 SHOC 基准测试 NVIDIA SDK,这些基准测试是 MultiCL 运行时的一部分。仅当系统配置发生更改时(例如,在系统中添加新设备或从中删除设备)时,才需要再次运行基准测试。


内核分析器通过性能建模或性能预测技术来估计内核执行时间。MultiCL 为了减少内核分析器所带来的时间开销,采用每个设备运行一次微型内核,并将相应的执行时间存储为内核配置文件的一部分。该微型内核由微型仿真技术创建,并将其调度到系统中的每一个参与计算的设备上运行,该内核划分单个工作组工作项,并记录该微型内核在每个设备上的相对性能。这种方式会给当前程序带来潜在的运行时开销,但是 MultiCL 采用的微型内核在运行时优化后,实验验证其运行时开销很小,有时可以忽略不计。同时该方式也能较为准确的反映内核在不同设备上的运行差异。微型内核创建发生在 OpenCL clCreateProgramWithSource 和 clBuildProgram 命令期间。MultiCL 运行时拦截 clCreateProgramWithSource 调用,并为每个内核创建一个迷你内核对象,通过拦截 clBuildProgram 调用,将带有新微型内核的程序构建为单独的二进制文件。尽管此方法使 OpenCL 的构建时间增加了一倍,但作者认为这是初始设置成本,不会改变程序的实际运行时间。


任务调度器将获取每一个 OpenCL 命令队列,并将关联的命令队列添加到就绪队列池中进行调度。MultiCL 仅通过概要设备信息文件和内核配置文件得到任务映射方案。MultiCL 运行时读取每个队列的聚合内核配置文件,任务调度器采用使命令队列内核执行时间最短的策略来确定理想的队列-设备映射,最大限度地减少并发执行时间。动态调度方法保证了理想的内核-设备映射,同时 OpenCL 平台下的设备数量不多,因此调度产生的开销可以忽略不计。调度程序将命令队列映射到设备后,该队列将从队列池中删除。

3 总结

OpenCL 首次提出了面向异构系统通用目的并行编程的开放式标准,适用于跨 CPU、GPU 和其他处理器的异构混合编程。而 MultiCL 通过对 OpenCL 标准的扩展,以在上下文全级别和命令队列本地级别进行静态和动态调度。MultiCL 是一个运行时系统,通过设备分析器,内核分析器和任务调度器完成了 OpenCL 的动态调度,同时保留了 OpenCL 本身由开发人员指定的静态调度方法,MultiCL 运行时优化使开发人员能够专注于应用程序级别的数据和任务分解,而不是设备级别的体系结构详细信息和设备调度,极大减轻了 OpenCL 开发难度,为以后的任务调度研究提供了良好的软件支持。但是,MultiCL 也引入了预执行开销,降低了程序执行效率,同时其预执行评估方案难以反映内核实际运行状态,尚待进一步优化。


作者:京东物流 靳紫薇

来源:京东云开发者社区 自猿其说 Tech 转载请注明来源

发布于: 刚刚阅读数: 5
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
OpenCL任务调度基础介绍 | 京东物流技术团队_gpu_京东科技开发者_InfoQ写作社区