1.4.2 使用CPU实现机器学习算法和并行加速
在计算机中,最通用的运算部件是CPU的ALU(Arithmetic Logic Unit,算术逻辑单元),一般每个CPU Core都只有一个ALU。只要计算机的CPU是图灵完备(Turing Complete)的,CPU中的ALU就一定支持乘法指令和加法指令,也就可以完成向量卷积运算。
背景知识:图灵机与图灵完备
1936年,著名计算机科学家Alan Turing(阿兰·图灵)发表了论文On Computable Numbers,with an Application to the Entscheidung's problem,并在该论文中提出了一个计算模型——图灵机。该论文的核心观点是,只要一台计算机能够实现图灵机的功能,就能够完成所有的计算任务,也就是“图灵完备”的。
图灵机在结构上包括以下几部分。
• 一条足够长的纸带(tape):纸带被分成一个个相邻的格子(square),在每个格子中都可以写至多一个字符(symbol)。
• 一个字符表(alphabet):即字符的集合,包含纸带上可能出现的所有字符。其中包含一个特殊的空白字符(blank),意思是在此格子中没有任何字符。
• 一个读写头(head):可理解为指向其中一个格子的指针。它可以读取、擦除、写入当前格子的内容,也可以每次都向左/右移动一个格子。
• 一个状态寄存器(state register):追踪每一步运算过程中整个机器所处的状态(运行/终止)。当这个状态从运行变为终止时,运算结束,机器停机并交回控制权。
• 一个有限的指令集(instructions table):记录读写头在特定情况下应该执行的操作。
在计算开始前,纸带可以是完全空白的,也可以在某些格子里预先写入部分字符作为输入。在运算开始时,读写头从某一位置开始,严格按照当前格子的内容和指令要求进行操作,直到状态变为停止,运算结束。而后,在纸带上留下的信息,即字符的序列(比如“...011001...”),便作为图灵机的输出。
Alan Turing指出,面对一个问题,对于任意输入,只要人类可以保证算出结果(不管花多少时间),图灵机就可以保证算出结果(不管花多少时间)。而图灵完备性是针对一套数据操作规则而言的概念。数据操作规则既可以是一门编程语言,也可以是计算机里具体实现了的指令集。当这套规则可以实现图灵机模型里的全部功能时,就称它是图灵完备的,即包括足够的内存、if/else控制流、while循环等在内的一系列工具。
基本上,目前常见的CPU都是图灵完备的。
然而,如果利用CPU的通用乘法指令和通用加法指令进行向量卷积等简单且重复的运算,那么在运算效率方面并不是一种好方案。这是因为,每个CPU Core都只有一个ALU,如果用于进行简单且重复的运算,那么即使ALU再强大,也只能串行执行指令。
因此,采用有并发运算能力的协处理器来代替CPU进行这种简单且重复的运算,就成为业界的共识。
Intel和AMD等CPU厂商的思路是,在ALU中增加SIMD(Single Instruction Multiple Data,单指令多数据)运算单元,在一条指令中计算多个数据,来加速此类运算。1997年,Intel推出了MMX(Multiple Media eXtension,多媒体扩展)指令,并在P55C(商品名为Pentium MMX)这一代处理器中落地。
MMX指令使用了8个新引入的MMX寄存器MM0-MM7,每个寄存器都是64bit的,可以拆分为8个INT8、4个INT16或2个INT32。单条MMX指令可以在2个指令周期内进行2个MMX寄存器的算术运算。这对于当时火热的计算机多媒体应用,比如MP3播放(傅里叶逆变换算法)、MPEG解压(离散余弦逆变换算法、YUV到RGB转换算法)、JPEG图像算法(颜色变换、DCT和霍夫曼编码等),起到了显著的加速作用。在Intel Pentium MMX处理器上运行的多媒体类应用,其CPU占用率显著降低,视频播放也更为流畅。
图1-4所示就是最早支持MMX指令的Pentium MMX系列处理器的经典款型——Pentium MMX 166。
图1-4
MMX指令的推出,使Intel取得了商业方面的巨大成功。Intel又在1999年发布的Pentium III处理器中引入了新一代SIMD指令集:SSE(Streaming SIMD Extensions,单一指令多数据流扩展)。SSE一次最多可以对8组16bit数进行运算,比如在一条指令中计算8次16bit浮点数相乘,在另一条指令中对得到的8个乘积进行累加,这样,也就可以在2条指令中完成有8个16bit浮点数元素的向量卷积运算。
我们注意到,CPU的SIMD指令可以对向量卷积运算及其他向量运算进行一定的加速,但受到CPU实现的限制,其加速比(并发执行运算操作的数量)难以超过16(128bit寄存器只能储存16个打包的8bit数据)。因此,工程师们想到了使用GPU进行此类运算。