对于嵌入式软体而言,程式码尺寸是越小越好。压缩程式码以适应受到成本或空间限制的储存子系统已经成为嵌入式系统开发的一项重要事务。ARM、MIPS、IBM以及ARC都提供了降低记忆体占用的技术,本文将对这几种架构中程式码压缩技术的实现进行比较分析。
如今,储存子系统的成本高于微处理器已不再稀奇。因此,选择一款能节约储存成本的处理器就变得很有意义。编写紧密的程式码只是事情的一个方面,而处理器的指令集对记忆体的消耗同样影响很大。对于程式码密度很差的处理器而言,无论怎样绞尽脑汁地去压缩你的C原始程式码都于事无补。如果你关注记忆体的消耗,选择恰当的处理器并精心调整程式码是明智的。
并不是所有的处理器都拥有或需要程式码压缩,只有32位元的RISC(精简指令集电脑)处理器需要程式码压缩,因为RISC处理器程式码密度较差。RISC处理器在过去是设计用于通用电脑和工作站,在其设计时认为记忆体价格便宜。尽管记忆体价格可能便宜,但如果能占用更少的记忆体不是更便宜吗?对于行动电话以及其他成本控制严格的嵌入式系统应用而言,在RAM或ROM上5美元的成本差异,就能导致量产时利润的巨大差别。通常,记忆体的大小是固定的,而产品的功能特性却各异。紧密的目标程式码意味着可以实现更多的自动拨号、更好的语音识别能力,或者可能是更清晰的萤幕显示。
在32位元嵌入式处理器中,ARM、MIPS以及PowerPC曾是首先寻找出降低其记忆体消耗、提高程式码密度方法的几种处理器。更早一些的处理器,如摩托罗拉的68k系列以及英特尔的x86系列,并不需要程式码压缩。事实上,其标准程式码密度都比 RISC处理器的程式码压缩模式还要高。
易于使用的Thumb技术
我们先从ARM的程式码压缩方案(Thumb)讲起,因为其使用广泛、有很好的支援,是一个典型处理器程式码压缩方案,并相当简洁、有效。
Thumb 实际上是添加到ARM的标准RISC指令集之上的独立指令集。在你的程式码中,你可以透过一条模式切换指令在这两种指令集之间进行切换。Thumb指令集架构(Instruction Set Architecture,ISA)是由大约36条16位元指令组成,仅靠这些指令是完成不了太多任务的,但Thumb指令集包括了基本的加法、减法、循环移位以及跳转指令。透过使用这些较短的指令替换ARM标准的32位元指令,可以将某些程式码的规模减少大约20%到30%。但有一些问题需要引起注意:
首先,Thumb程式码和标准ARM程式码不能混杂使用,必须显式地在两种模式间进行切换,就好像Thumb是一套完全不同的指令集(实际上也是)。这迫使程式设计人员将所有的16位元程式码与32位元程式码分开并隔离到独立的模组中。
其次,由于Thumb是经过简化和精简的指令集架构,在Thumb模式中无法完成所有你希望的工作。Thumb模式无法进行诸如处理中断、长跳转、原子记忆体(atomic memory)作业,或协同处理器作业等。Thumb有限的指令意味着仅对基本的算术和逻辑作业有用,其他的任何工作必须使用ARM的标准32位元指令集来完成。
Thumb的限制不仅表现在指令集上,当处于Thumb模式中,ARM处理器将仅有8个暂存器(而不是16个),这些暂存器无法像标准模式下ARM程式码那样进行条件执行和移位或循环移位作业。在标准ARM程式码和Thumb程式码间进行参数传递并不困难,只要将参数放到堆叠中或透过处理器的前8个暂存器就可以了。
从标准模式到Thumb模式之间的来回切换也要消耗时间,而且还要增加程式码。此外,还需要几十个前导(preamble)以及后同步指令(postamble)来组织指针并清空CPU的管线。如果在Thumb模式中执行的程式码小于几十条指令,就不值得为之付出这样的开销。
最后,Thumb还对于性能有着少许的影响。通常,使用Thumb指令对程式码进行压缩会导致程式码执行速度降低大约15%,这主要是由于在16位元模式和32位元模式间切换所引起的。Thumb指令还不如32位元的标准指令灵活,因此,和32位元程式码相较,常常需要更多的指令来完成同样的工作。从积极的一方面来说,由于其指令长度只有32位元指令集的一半,Thumb使得缓冲记忆体的使用效率更高。
如果任务能够在这些限制下完成,Thumb可以节约不少成本。Thumb技术已经得到每一款ARM处理器的支援,无论用户使用与否,多数ARM编译器以及汇编程式都支援Thumb指令集。因此,采用Thumb的体验应该相当轻松。
MIPS处理器
理解了Thumb技术后,MIPS16e就没什么新奇的了。一些MIPS处理器中增加了另外的16位元指令集,与ARM系统非常类似。MIPS16e指令集包括了一组16位元的标准MIPS演算法、逻辑以及跳转指令的简化版本。其使用和Thumb一样,也需要在标准模式和MIPS16e模式之间来回切换,这也将导致付出时间和增加程式码的开销。除非能在‘压缩’模式上执行相当长时间,否则没有必要进行模式切换。其程式码压缩效率和ARM差不多,对于多数程式而言,也是20%到30%。
MIPS16e和Thumb都不能对程式码进行真正的压缩,它们只是对部分指令提供了可替换的作业码,而且得到的压缩比是依赖于短作业码和长作业码的总长度的比值。也就是说,依赖于程式码所完成的任务,诸如作业系统和中断处理例程等系统级程式码根本就不能使用16位元指令,因此不能获得程式码压缩效果。一般的演算法,只要不使用任何大作业数,就能得到很好的压缩效率。最后,别忘了数据是无法进行压缩的,只有程式码能够被压缩。如果你应用程式码中包括了大量的静态数据结构,所能得到的总记忆体节约是非常小的。还有,15%的性能损失也许很不值得。另一方面, MIPS16e和Thumb都是免费的(假定你的处理器已经包含了它们),选用它们的成本非常低。
PowerPC的CodePack技术
值得提前说明的是,IBM的CodePack方法是各种程式码压缩技术中最复杂的。与Thumb和MIPS16e不同,CodePack系统是真正对执行程式码进行压缩,就好像在PowerPC软体中执行WinZip。CodePack会分析并压缩整个程式,产生的用户程式码必须在执行中解压缩并执行压缩版本。尽管很复杂,CodePack和其它技术一样提供20%到30%的空间节省。
CodePack是一项很有吸引力的技术。要使用该技术时,只须和平常一样使用标准工具编译嵌入式PowerPC程式码就行,CodePack甚至对已有的程式码也能使用(无论有没主动程式码)。在将程式码写入ROM或装入磁碟之前,执行CodePack压缩工具对程式码进行压缩。压缩工具会分析程式码指令的分布并产生一对专门针对这个程式码的键值。当执行压缩后的程式码时,拥有CodePack功能的处理器使用这一对键值来在执行中解开压缩的程式码,就好像直接执行压缩后的程式码。解压缩会对处理器的管线产生很小的延迟,但是其影响被取指延迟以及其它延迟所掩盖。对于绝大多数应用,CodePack带来的性能影响是可以忽略的。
但是,CodePack还有一些其它的影响。由于每一个压缩的程式都有其单独的压缩键值,CodePack本质上既是压缩系统也是加密系统。没有键值,无论你自己还是其它任何人都无法执行相应的程式。如果丢失了或者未获得相应的键值,压缩后的程式只是一堆无用的乱码,这也意味着压缩后的PowerPC程式不是二进制程式码相容的。除非同时包括其解压缩键值,否则无法轻易地和其它系统交换压缩后的程式。这会使嵌入式系统软体的现场分配稍微有些复杂。
另外,CodePack为每个程式产生两个键值是因为指令的高16位元和低16位元是分别进行压缩的。IBM的工程师发现每一条PowerPC指令的高半字 (作业码就在其中)和低半字(其内容通常为作业数、偏移量或遮罩)的分布频度是不一样的。对它们分别使用不同的压缩演算法会使压缩效果比仅使用任何单一演算法要好,这就是CodePack对程式所做的事。
ARCompact
ARC International公司又采用了另外的程式码压缩方法。因为ARCtangent处理器有用户可定义的指令集,ARC(及其用户)可以对指令集进行随心所欲的修改。作为ARCompact,ARC公司决定加入一组16位元指令来改进其处理器的程式码密度。
而ARCompact 与Thumb以及MIPS16e的区别在于可以将16位元程式码和32位元程式码任意混杂。由于没有模式切换,程式码中任意分布的少许16位元指令无须为之付出什么开销。在任何可能的情况下,ARC的编译器的默认配置会产生16位元作业(为了强制编译器产生32位元程式码或与旧的处理器保持相容,你可以关掉这个功能)。
ARC可以混合不同长度程式码而不必付出相应的开销,是因为其指令架构要比ARM和MIPS新。那些RISC架构的指令集(包括PowerPC)在指令字中没有指明指令长度的位元。诸如ARC或Tensilica的新伪RISC架构,以及像x86和68k旧的架构拥有这些位元。无论是出于无意还是远见,变长度指令架构因为更紧密的程式码而具优势。
Thumb的改进版-Thumb-2
就在最近,ARM对其程式码压缩系统进行了改造并发布了Thumb-2。Thumb-2并不是Thumb的升级,相反,它是另起炉灶,并且可以完全代替 Thumb和原先的ARM指令集。Thumb-2有些类似ARCompact或摩托罗拉的68k,可以无需模式切换就执行16位元与32位元混合程式码。总的来说,Thumb-2提供的程式码压缩效率要略差,但其性能损失也较小。
为了做到这一点,ARM需要在其作业码映射中找到一个突破口(hole),他们在BL指令(条转并连接指令,是Thumb和ARM模式之间切换的指令)中找到了需要的突破口。在原有的指令集中,BL指令有一些位元没有使用,这些原先未定义的位元给全新的指令集提供了切换入口。其编码确实不怎么样,但确实很有效。
Thumb-2最大的优势在于它是一套完整的指令集,程式无需切换回‘标准’32位元ARM模式,原先Thumb模式的限制再也没有了。程式现在可以处理中断、设置MMU、管理缓冲记忆体,和真正的微处理器并没什么不同。
Thumb-2还是需要损失一定的性能。尽管没有了模式切换开销,与标准ARM程式码相较,它还是要花费多一些的Thumb-2指令来完成特定的任务。对于ARM处理器而言,这些额外的指令(以及额外的周期)会使速度降低大约15%到20%。
未来的ARM处理器最终将只执行Thumb-2程式码。由于它用一套单一的更多压缩指令的指令集有效地取代了ARM和Thumb指令集,为什么不会最终彻底取代它们呢?但问题是ARM的软体相容性怎么办?直到现在,所有的ARM处理器(除了Intel的XScale)都是二进制相容。尽管支援Thumb-2 的新型处理器将能执行现有的ARM和Thumb程式码,但反之则不行。当Thumb-2广泛推广后,它将制作一套单独但等价的软体库。
作者:Jim Turley
独立分析师
Email: [email protected]