编者按
经常有软件的同学会问到一个尖锐的问题:在超异构软硬件融合的时代,操作系统等软件是不是需要重构,是不是要打破现有的整个软件体系。我赶紧解释:“超异构软硬件融合不改变现有的软件体系,所有的软件该是什么样还是什么样。”
当然了,上层不改变,不意味着底层不调整。虽然可以“躺平”,在超异构计算平台直接复制现有的软件架构;但要想发挥超异构计算平台的强大性能,底层软件做一些调整也是必然的(当然,这些调整最好是润物细无声的渐进式迭代)。
底层软件最核心的是操作系统。因此,引出了我们今天要讨论的话题:在超异构计算时代,操作系统架构会有哪些改变?
本文来自个人的一些思考以及和周围朋友们的一些探讨,我不是操作系统专业出身,班门弄斧,抛砖引玉,希望得到各位专业人士的指正。
操作系统是管理计算机硬件、软件资源,并为应用程序提供公共服务的系统软件,是计算机系统的内核与基石。操作系统需要:管理与配置内存、决定系统资源供需的优先次序、控制I/O设备、操作网络,以及管理文件系统等基本事务,也需要提供一个让用户与系统交互的操作界面。
一个标准的操作系统通常提供以下功能:进程管理(Processing management)、内存管理(Memory management)、文件系统(File system)、网络通信(Networking)、安全机制(Security)、用户界面(User interface)和驱动程序(Device drivers)等。
经典的冯诺依曼架构告诉我们,计算机是由五部分组成的:控制器、运算器、存储器、输入设备和输出设备。我们可以把这个模型的组成部分再简化一下,计算机是由三大部分组成的:
所有的软件都是在CPU处理器上运行的。
软件在CPU的运行,主要有两种作用,一种是硬件的管理(控制面),一种是硬件的使用(计算/数据面):
图 单核系统的用于调度的队列框图
多进程宏观并行、微观分时调度是现代操作系统最显著的特征。同一时刻,在内存中有多个进程/线程程序,它们分别处于运行态(在CPU中运行)、就绪态(在内存等待)、阻塞态(在内存挂起)。根据事件触发以及进程/线程的状态,也根据调度算法来选择要进入执行状态(送到处理器运行)的进程/线程,也决定了每个进程的状态更新。
在现代操作系统里,每个进程会包含一个或多个线程,进程作为资源分配的最小单位,线程作为任务调度的最小单位。
多核任务调度,最简单的是复用单处理器调度的基本架构,将所有的工作任务放入一个单独的队列。但这种方式扩展性不好,多核调度的时候需要频繁地给队列加锁。锁会带来巨大的性能损失,并且随着CPU核的数量增加而调度的性能指数级下降。还有一个问题,是调度可能会引起线程在不同的处理器运行,这会导致在CPU缓存中的程序现场需要跨CPU访问,从而导致性能的下降。
于是,有了多队列任务调度,比如给每个CPU核创建独立的任务队列,分别调度。每个CPU调度之间相互独立,就避免了单队列方式中由于数据共享及同步带来的问题。多队列任务调度,具有更好的扩展性,随CPU核的数量增加,锁和缓存跨CPU访问的问题不会扩大。但多队列也会有新问题,即负载可能会不够均衡。所以,就需要任务在不同的队列迁移,从而确保所有的CPU负载足够均衡。
从单核串行到(同构)多核并行,再从同构的多核并行到异构的多核并行。而典型的异构多核也有CPU+GPU以及CPU+DSA两大类模式。
超异构计算指的是多种异构计算的融合,最终形成CPU+GPU+多个不同类型DSA以及其他各种可能的处理器类型的模式。
因此,我们可以把计算架构的发展分为如下几个阶段:
我们在上一节的超异构计算机的功能模块图基础上,加入任务调度的示意信息,超异构操作系统的任务调度包含三部分:
根据1.1节的经典操作系统分层架构,我们可以给出一个典型的超异构操作系统的分层架构图。除了经典计算机的各种功能组件之外,还需要加入GPU、各类DSA的相关软件栈。
以硬件资源为单位的独立的软件堆栈和经典计算机操作系统以及添加了异构计算软件框架的内容是基本一致的。如果不考虑性能优化的话,可以复用现有的技术栈。但这样能提升的性能非常有限,这跟SOC中的多异构软件级协同没有本质区别。
随着性能要求越来越高,也随着硬件资源类型和数量越来越多,不同硬件资源之间的交互问题会凸显。需要考虑基于硬件所形成的垂直软件栈之间的协同,才能最大限度地释放超异构平台的性能优势。
加速处理器的运行,必然是需要有Host CPU的上层软件的控制和协作;加速处理器要想更加高效的运行,把性能优势发挥出来,并且让软件人员更容易使用,就需要有功能强大的软件框架,也需要形成相应的生态。例如,强大的NVIDIA GPU,离不开CUDA软件框架和生态。理想化的,这个框架需要足够开放标准,并且最好能集成进主流的操作系统,比如Linux。
框架承上启下:从框架往下,硬件设计者需要自己的硬件支持主流开发框架,才能使得自己的产品在客户的业务场景低门槛的使用起来;从框架往上,软件开发者不需要关注硬件细节,只需要关注自己的业务创新,把精力投入到更高价值的工作。
传统的开发思路是“从下而上,硬件定义软件”:比如x86做得足够的好,所以才有了基于x86平台的工具链以及成熟的商业应用软件等强大的软件生态;再比如NVIDIA的GPU和CUDA,基于自己的GPGPU,构建了足够好的CUDA框架。上层的软件开发者都是基于这些硬件平台来构建自己的软件。
随着行业的发展,现在逐渐地过渡到了“自上而下,软件定义硬件”的阶段:通常是,用户的业务已经在x86 CPU平台完成了开发,并且整个业务系统已经得到了充分的验证,业务逻辑也是相当的确定。用户希望自己掌控系统的一切,硬件只是整个系统的运行平台而已。客户面临性能和成本的压力,于是希望从x86迁移到ARM或RSIC-v,或者通过硬件进行性能加速——但绝对不希望改变自己已有的软件业务逻辑!
最终,扮演关键角色的会是框架:
在框架的约束下,各类加速处理器的架构需要逐步收敛,走向标准化。
这里再强调一下“架构”的概念:架构指的是硬件呈现给软件的接口,也即软件视角看到的硬件;而硬件实现的架构通常称为微架构。
在SOC平台上,所有的硬件加速器是通过软件联系到一起的,如上图的绿色连线部分。这样的方式有如下一些问题:
需要构建一套快速的内存旁路机制,让加速处理器的处理结果不需要回到内存,而是直接的发送给下一个处理器,如图中橙色连线部分。
当然,这里只是对这个问题的简单示意,实际的问题远比这个示意情况复杂的多。
当处理器的类型越来越多,应用也需要有一定的适应性:
应用跨处理器类型的价值体现在:
要实现应用跨不同类型处理器运行,需要在框架层面做很多工作。这块的技术和知识,可以参考Intel的oneAPI。
oneAPI是开源的跨平台编程框架,底层是不同的XPU处理器,通过oneAPI提供一致性编程接口,使得应用跨平台复用。
在数据中心,大家进行资源和算力扩展的时候通常有两种方式,一种是提升单位设备的能力(Scale up),一种是提高设备的数量(Scale out)。
在之前文章中讲到算力提升的时候,我们也给出了一个公式:“实际总算力=(单处理器芯片)性能x处理器芯片数量x利用率”:
(全文完)