在全球爆炒GeForce RTX 3000系显卡的同时,笔者也开始重新思考除了8K Gaming外让N卡大放光彩的特性,一个让英伟达即使受到Intel和AMD的分庭抗礼,却仍然在多媒体生产,数据科学,大规模计算上仍然鹤立鸡群的独特技术。
笔者指的就是CUDA,它的全称是:
Compute Unified Device Architecture
统一计算设备架构
是英伟达研发的计算平台和编程模型,它利用并行计算原理在GPU上进行通用计算。
CUDA允许计算机在运行支持显卡加速的应用时,让序列性(sequential)程序运行于更加擅长单线程运算的CPU,并让偏重重度计算的部分并列运行在成百上千的GPU核心。因此用CUDA编写的程序能充分利用 GPU 的力量以大幅提高计算性能(只能在英伟达的GPU上运行)。
目前CUDA基于多种语言,支持C++,Fortran,Python和MATLAB等,实现起来也随着API的逐步发展而日渐简单。
笔者一年多前曾购买过英伟达的单片机Jetson Nano,操作系统有一个集成的CUDA ToolKit,用来写一些简单但实用的程序,乐趣无穷。Jetson Nano只有192个流处理器(CUDA Core),但已经可以在机器学习,数据处理和一些基本操作上发挥出比较可观的性能优势。
笔者相信,这项技术,将在未来数年在相关领域发挥极其重要的作用。今天开始,笔者将从基础技术开始,温习一下这项技术的各个方面。
Speed is irrelevant if you are running the wrong direction, especially when you are alone.
下面先扯一些CPU的事情,下一章再说GPU,以及CPU和GPU计算的差别。
英特尔三剑客:安德鲁·葛洛夫 Andrew Grove 罗伯特·诺伊斯 Robert Noyce 和 戈登·摩尔 Gordon Moore
戈登·摩尔有一句至理名言:
每过一年,半导体处理器核心的时钟频率都会翻番
这一定理般的话,一直持续到近些年。如今,处理器单核的时钟频率已经达到了饱和(所以你不太会找到一个单核频率5GHz的处理器,即使是在不远的将来),处理器的范式也逐渐演变为了多核心。
并行处理这一技术应运而生,登上历史舞台。
正如计算机科学原理所说,在现代计算机中,一条指令总结起来需要大致五步:
这就是基础的五步RISC架构(five-stage RISC architecture)
用CPU实现并行处理有多种方法。首先,是指令级平行运算 ILP(Instruction Level Parallelism),也被称为Pipelining
CPU管线
允许CPU在一个时钟周期内可以同时运行的一系列指令
一个不兼容ILP的中央处理器会逐步进行上述的五个步骤:
取指令 - 解码指令 -> 执行指令 -> 访问内存 -> 写回寄存器
因此,完成一条指令至少需要五个时钟周期。不仅如此,同时芯片的其他部分都在等待该指令执行完毕。
非常低效
这就是CPU管线化(CPU Pipelining)所要解决的问题。应用Pipelining,在一个时钟周期内,多个指令的多个进程在同时运行。这也照应了它在业内标准的名字:Instruction Level Parallelism 指令级平行运算
指令级平行运算 的基本流程
用管线化的指令,指令通量增加了,计算机也得以在一个时钟周期内处理多个指令。但是因为ILP本身的性质,处理器的资源还是存在闲置。
在管线芯片中,指令通量增加了。之前,一个指令花五个时钟周期才能完成。而之后,从第五周期开始,每个周期末尾都有一个指令执行完毕(假设每步指令占用一个循环)。
注意,在非管线的芯片被认为指令之间是相互独立的,因此不会有数据危险。而在管线芯片中,数据危险是存在的。
比如:I1 - ADD 1 to R5 I2 - COPY R5 to R6
在一个管线芯片中,I1(第一条指令,下同) 从 t1(第一时钟周期,下同) 开始,到 t5 结束。I2从t2开始,到t6结束。整数1 在 t5(WB 阶段)添加到 R5 中。第二个指令在第二步(时间 t3)读取 R5 的价值。因此,它不会获取更新后的值,这会带来危险。
现代编译器会将高级语言解码为低级语言,并处理如以上这种危险。
指令级平行运算 (ILP) 还可以通过实施超标量体系架构来实现。和管线处理器的主要区别是(超标量体系处理器也属于管线处理器):
超标量体系架构使用同一芯片上的多个执行单元实现 ILP,而管线处理器(UE)则将执行单元分多个阶段来实现。
这意味着,在超标量体系架构中,多个指令可以同时处于执行周期的同一阶段。这是绝对不可能在一个简单的管线芯片实现的。超标量微处理器可以同时执行两个或两个以上的指令,相对应的,他们通常至少有2个ALUs。
超标量处理器可以在同一时钟周期中发送多个指令。这意味着多个指令可以在同一时钟周期中开始。如果你看上面的管线架构,你会发现在一个时钟周期中,管线处理器只能发送一个指令。超标量体系架构的情况并非如此。但我们只有一个指令计数器(在运行过程中,多个指令被跟踪),这仍然只是一个进程。
以英特尔i7-10700为例。处理器拥有 6 个独立内核,每个内核都实现完整的 x86 ISA指令集。每个内核都带有 2 个物理核心。
Hyperthreading是一种令人兴奋的技术,也是英特尔的专有技术。操作系统使用该技术将单个内核视为两个虚拟内核,以增加管道中的硬件指令数量(请注意,并非所有操作系统都支持 HT,英特尔建议在这种情况下禁用 HT)。综上所述,上述英特尔 i7 共有 6 个物理核心和 12 个线程。
HyperThreading 只是一种更好地利用处理器内核的技术。很多时候,处理器核心只使用其资源的一小部分来执行指令。HT 所做的仅是,它用多个CPU寄存器 ,并在处于闲置的核心部分执行更多指令。因此,一个内核可以显示为两个逻辑核心,但应当认为它们并非完全独立。如果两个“核心”都需要访问 CPU 资源,则其中一个必须等待。
这就是为什么我们不能用超线程单核 CPU 取代双核 CPU 的原因。
双核 CPU 将拥有真正独立、不需要遵循一定秩序的核心,每个核心都有自己的资源。
另请注意,HT 是英特尔实现 SMT(同时多线程)。SPARC(一款有可扩展性功能的微处理器) 对 SMT 的实现方式不同,目标相同。
CPU 使用类似于超线程的技术实现 SMT。因此,它能够同时运行两个不同程序(红色和黄色)。
因此,多核CPU是合理的存在。需要注意的是,CPU旨在固定顺序程序。CPU 在单个基准上执行单个指令时非常给力,但在处理大量数据时就没那么优越了。
以上就是一些关于现代计算机CPU的知识。
CPU 具有比 GPU 更大的指令集、复杂的 ALU、更好的分支预测逻辑以及更复杂的缓存/管线方案。指令周期也快得多(你的英特尔处理器可能有4GHz+, 但即便RTX3000系卡也多在2GHz左右)。
笔者不是学这个的,如果以上所写,有什么谬误,大家一定指正一下,言辞激烈可以接受。