第2章 HSA概述

HSA在过去三十多年中,已经成为将CPU内核和CPU套接字绑定在一起的共享内存系统体系结构。HSA最初专注于将GPU有效地用作CPU的并行协处理器。后面意识到在GPU上高效运行所需的体系结构特征也适用于许多不同类型的专用处理单元,其中许多已经存在于SOC中。

GPU最初是作为IO设备连接到CPU的。HSA基金会成立于2012年,当时SOC内部的GPU核心仍然很难编程。HSA基金会成立是为了解决这个问题,为SOC上所有类型的处理单元的体系结构整合铺平道路。

2.1 GPU计算简史:HSA解决的问题

GPU单独的内存池为程序员带来麻烦,因为数据必须在内存池之间移动,这取决于接下来哪一个处理器将操作数据。系统内存通常很大,但是不提高很高的带宽。GPU内存通常很小,但带宽很高。在多处理环境中具有多个内存池的系统通常被称为NUMA。

除了多个内存池问题之外,传统GPU使用与CPU完全不同的虚拟地址空间,用于相同的应用程序或同一进程内的访问。意味着系统内存的一块数据位于CPU的地址A时,同一内存由GPU以完全不同的地址B访问。这意味着一个地址不能再CPU和GPU之间传递,也不能在任何地方立即解引用。相反,软件必须介入并执行地址转换。更糟糕的是,早期的GPU完全根据内存缓冲区内的偏移量工作,而不是使用完整的地址或指针。

下一个要解决的问题是内存分页。CPU可以在所有的系统内存中随意操作,如果操作系统已经从系统内存中删除一个页面,CPU会在继续之前通知操作系统检索该页面。相比之下,GPU只能在先前由操作系统“页面锁定”或“钉住”的系统内存页面中操作。这意味着更早的应用程序必须进行操作系统调用来锁定这样的内存页面,以防止操作系统将其分页。操作系统通常有一个策略,不允许超过一半的系统内存页以这样方式固定,以保持系统响应。这限制了GPU可以使用的系统内存数量。并依赖于程序员提前标识出GPU后面可能访问的所有页面并将其锁定。

GPU的最终内存问题是一致性。多核CPU中跨SMP套接字的CPU内核彼此缓存一致。这意味着如果数据已经被一个CPU写入地址A,并且数据在其缓存中,而没有写到内存,然后另一个CPU从地址A读取,系统硬件需确保读取到最新的数据。

在构思HSA时,GPU能够探测CPU高速缓存,但CPU不能探测GPU高速缓存。实际上,GPU上的CU甚至不能够探测内部其他CU的缓存。相反,软件负责批量运行GPU作业,并记住在作业或批处理之间刷新缓存,以使结果对系统的其余部分可见。这意味着GPU只能实现非常粗粒度的一致性,这限制了它们可以运行的工作类型。

早期GPU计算开发者面临的下一个问题是启动GPU任务固有的开销。通常情况下,GPU处理的任何数据来自于CPU。所有数据输入缓冲区都必须从CPU系统内存复制到GPU内存。接下来,在GPU上执行任务的命令必须比组合并转换成GPU可以获取和解码的内存形式。这是通过OpenCL和CUDA中的排队API函数来实现的,该函数将通用的命令包转换为最终硬件,从而将其专用于GPU供应商。然后,使用操作系统调用将命令包放入一个单独的队列中。随后,所有生成的结果将从GPU内存复制到CPU内存,以使其可供CPU上运行的其他程序使用。通常,这个开销非常高。

对于CUAD和OpneCL等语言和编程模型,通常包含两个元素:一个带有API的运行时库和一个基于C语言非常有限的子集的GPU“内核语言”。大多数程序员很难再同一个项目中使用第二种语言编写模块,这进一步限制了GPU计算的使用。

需要GPU计算专用语言所带来的一个微妙问题是对“双源编程”的要求。意味着加载到GPU的所有模块都必须进行两次编码--一次以应用程序的主要语言编写,以便在CPU上运行,然后再次以GPU内核语言中以投机的方式进行加载。源代码的这两个副本通常主流在不同的源文件中,但必须保证其包含相同的功能,并始终为相同的输入数据产生相同的结果。双源开发式昂贵的、令人厌烦的且很难保证正确性。

传统GPU计算编程模型是专有的,只能在单一供应商的GPU上运行,大多数程序员不愿意为不同的供应商编写不同的代码。

早期GPU编程非常困难,因此在HSA基金会,着手解决所有这些问题,消除GPU作为CPU的真正协处理器的许多屏障,并为主流程序员提供简单的“单一来源”GPU计算编程。

使GPU计算编程更容易的下一个创新是创建SOC或APU。CPU、GPU和内存控制器在同一硅片上的物理集成是非常重要的一步。它将GPU放置在与CPU相同的系统内存上,但不幸的是,它没有在体系结构上统一CPU和GPU的内存空间。在许多情况下,系统内存的物理连续的“划出区域”在引导时被预留为GPU内存----在4GB系统上可能是512MB。相较于系统内存的其余部分,GPU内核通常可以更快地操作GPU内促,并且应用程序在使用之前不需要对其进行页面锁定。GPU内存池对于CPU来说是不一致的。GPU缓存保持非一致性。系统对于两个内存池仍然具有完全独立的虚拟 地址空间,并且对于每个内存池具有不同的带宽。尽管CPU和GPU在同一个内存控制器上,但NUMA系统体系结构仍然存在。GPU仍然局限于访问少量内存,系统内存和GPU内存之间的拷贝是常见的。

如果没有完整的内存一致性,只有一个普通的地址空间以及GPU引用指针的功能,程序员只能在OpnenCL和CUDA中进行编程,而将其留在双源“地狱”中。

统一内存做法是真正拥有一个内存池,不需要为GPU开拓单独内存区域,而GPU核心的设计则是将全部性能提供给系统内存。此外,GPU硬件升级与CPU具有相同的地址空间、与CPU完全一致、可操作分页内存、处理页面错误、使用实地址而不是内存缓冲区内的偏移量。最后,有一个指针指向要访问的处理器。这是难以置信的强大,因为它运行GPU直接运行在CPU数据结构上。相反,这又会导致高级语言被直接编译到CPU和GPU代码中,不再需要使用特定于GPU的语言、

HSA的一个重要方面是操作系统对它的支持是通用的。这意味着,一旦操作系统添加了对一种处理器类型(GPU)的HSA支持,则为了添加其他处理器类型(DSP、编解码器、FPGA和其他加速器),不需要进一步的操作系统更改。

2.2 HSA的支柱

2.2.1 HSA内存模型

前面讨论了HSA内存模型功能 ,包括统一寻址、分页内存、全内存一致性以及在不同类型的处理器之间传递指针的能力。本节进一步讨论支持处理器间直接协作的同步功能。

HSA模型包括平台原子操作的定义。这些平台原子是允许不同处理器类型在管理队列、同步工作和实现无锁数据结构方面进行互操作的关键。

HSA内存模型是一种松弛一致性内存模型,这是为了实现高吞吐量的并行性能,并与保存--释放和载入--获取语义、屏障和栅栏同步。获取和释放语义时影响执行线程之间加载和保存的可见性的规则。在一系列载入中的“载入-获取”保证不会对执行线程内的任何载入重新排序。相对于任何先前在执行线程中的保存,将保证保存-释放不会被重新排序。这允许普通的载入和保存在编译器或硬件的执行线程内重新排序,以获得最佳性能。载入--获取和保存--释放操作提供了排序同步,允许无锁编程,并在独立线程之间的内存中启用可靠的交互。

2.2.2 HSA排队模型

HSA排队模型旨在允许HSA代理之间的低延迟调度工作,包括两个基本的部分:
        1. 用户模式调度。这意味着HSA代理能够在调度程序的控制下从多个单独的队列中提取数据。用户模式队列的设计可以为每个应用程序创建一个单独队列,甚至可以为每个应用程序创建多个队列。每个应用程序都可以将调度包直接放置在自己的队列中,而无需使用运行时API或OS服务来管理共享队列。用户模式队列显著减少了应用程序提交工作所需的等待时间。此外,它们还能更好地支持高级调度员执行不同的服务质量策略。
        2. 创建了一个用于启动并行计算的标准包格式,称为体系结构排队语言AQL。意味着来自任何硬件供应商的任何HSA代理现在都可以执行AQL。这与传统系统不同,后者需要设备驱动程序或其他软件层将API数据格式转换为供应商特定的格式。

通过这两个步骤,典型的调度时间已经从毫秒下降到几微妙,大大提高了系统的效率,并允许较小的并行作业从加速中受益。

2.2.3 HSAIL虚拟ISA

HSAIL是并行计算例程或内核的虚拟ISA。HSAIL是一种低级别的中间表达,通常由高级语言编译器生成,这是独立于供应商和ISA的。在执行之前,执行终止器将HSAIL转换为待执行机器的ISA。

HSAIL终止器可能在不同的时间运行来适应这种情况。它可以在第一次应用程序运行时、在应用程序安装时、甚至是在编译器JIT运行。

2.2.4 HSA上下文切换

为了使异构计算变得真正无处不在,HSA应用程序需要在多个应用程序或进程同时运行的系统中运行良好。传统的GPU计算系统在多个程序同时调度GPU工作时,已经受到了服务质量问题的影响。如果其中一个程序使用不能长时间释放机器的计算内核或者使用大量的固定内存,则这一点尤为重要。

因此,HSA包括抢占和上下文切换的规范,以使OS能够抢占长时间运行的作业,保存其状态以便以后恢复,并切换到不同的作业,就像它对CPU的作用一样。

2.3 HSA规范

官网:www.hsafoundation.com/standards/

2.3.1 HSA平台系统体系结构规范

此规范记录了HSA硬件需求:共享虚拟内存、一致性域、信令和同步、原子内存操作、系统时间戳、用户模式排队、AQL、代理调度、上下文切换、异常处理、调试基础结构和拓扑发现。

2.3.2HSA运行时规范

HSA应用程序链接到该库以使用该平台。运行时包括初始化和关闭、系统和代理信息、内存管理、信号和同步、协调调度和错误处理的API。

HSA运行时域以前的所有计算运行时相区别的一个方式是不要求某个API必须用于作业调度。相反,运行时使应用程序能够设置自己的用户模式队列,并且可以以低延迟随意调度工作。

HSA运行时与以前的所有计算运行时相区别的一个方面是不要求某个API必须用于作业调度。相反,运行时使应用程序能够设置自己的用户模式队列,并且可以以低延迟随意调度工作。

2.3.3 HSA程序员参考手册----HSAIL SPEC

此规范还规定了称为BRIG的HSAIL的对象格式。这允许从单个源程序生成“胖二进制文件”。在这里,对于并行例程,编译器会将CPU对象代码和HSAIL代码生成到同一个二进制文件中。在加载中,根据HSA运行时中的发现例程来选择要执行的路径。

2.4 HSA软件

在传统的驱动程序栈中,在将命令包传送到硬件之前,从应用程序到硬件的每个命令都必须经过多个软件层,包括运行时、用户模式驱动程序、内核模式驱动程序和操作系统。相比之下,在HSA软件栈中,应用程序可以直接将命令包写入硬件队列,而无需通过任何其他软件层调用。在这个体系结构和内核模式驱动程序中仍然存在一个运行时,但是它们处于旁边,被调用的次数要少得多,以处理初始化、队列创建、内存分配和异常处理等事情。对于GPU任务调度,不需要调用HSA运行时。

这个软件栈有2个主要优点:
        1. 将工作调度到GPU的开销要小得多。
        2. 应用程序对执行有更多的控制,而且在发现驱动程序更新之后其性能配置文件已更改时不易受影响。

除了更高效的软件栈外,HSA还为程序开发者使用它们选择的语言编写要在GPU计算引擎上执行的代码打开了大门。

HSA软件栈本身就是一个开发源码的实现,大部分的HSA软件栈都是由AMD以开发源代码发布的,并已经公开发布。尽管终止器和内核模式驱动程序是特定于硬件供应商的,但其他组件(如HSA运行时、HSA辅助库、LLVM编译器和HSAIL代码生成器)是硬件独立的,可用于所有硬件供应商。

异构计算的开源栈有几个优点:
        1. 通用组件的单一开源版本避免了相同功能的不同实现,为应用程序开发人员创建了更统一的软件环境。
        2. 许多客户更喜欢开源软件,这样它们可以促进性能优化,执行自己的维护,而不会被固定到单个供应商。
        3. 开源代码为大学研究人员开启了大门,以探索这个令人兴奋的新领域并做出自己的贡献。

2.6 小结

本章介绍了HSA的概况,包括:

        1. HSA解决了那些问题;
        2. HSA如何在高层次工作;
        3. HSA的主要特点;
        4. 使用HSA的好处;
        5. HSA软件栈的主要功能;

探讨HSA提供高性能、低功耗和易于编程的真实示例,旨在协助读者更好地理解规范文档。

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值