Transmeta 的产品设计理念奠基于「让软体主导一切」,负责指令转译的 CMS(Code Morphing Software,代码变形软体)自然就是决定效能的成败关键,这让 Transmeta 的处理器看起来就像「黑盒子」,里面怎幺乱搞,外头都管不着。
软体搞定一切的产品设计思维
CMS 既然本质是迷你作业系统,势必占用部分系统主记忆体空间,存放在 Flash ROM 的 CMS 映像档体积 1MB(共有两份,一份用来当错误修复的备份),解压缩后载入记忆体 1.7MB,加上相关的资料结构与转译快取(Translation Cache),总计吃掉 16MB 容量,后来的 Efficeon 则加倍到 32MB。看到主记忆体被咬掉一小块,在 2000 年 128MB 记忆体还有点小贵的年代,其实让人感到有点小小的心痛。
事实上,Transmeta CMS 有几个少为人知的优点:
- 身为「软体」的 CMS 可随时改版,除了可持续最佳化提升效能表现,易于修正产品瑕疵,蕴含了支援「未来新增 x86 指令」的潜力,并可经由软体调节效能与功率,迎合市场需求。
- 只要确保可正确相容 x86 指令集,CMS 怎样无视 x86 指令集的种种先天限制(如精确中断)胡搞瞎搞都无所谓,可放手一搏,用尽一切挤出效能的手段,例如「删减真正被执行的指令数量」。
- 这是一般比较少人提到的优势:各位只要想想看「使用 Word 长时间写作、然后还用不到 Word 大多数功能」的场景即可理解,大多数人操作电脑的习惯,95% 以上时间集中使用极少数的应用程式与作业系统功能,真正动用到的指令码比例经常低于 10%、甚至低到 1% 都有可能。
CMS 可持续反覆「精鍊」转译这极少量的指令码,并利用有限的记忆体暂存区保存转译成果,使其「越跑越快」。这是传统纯硬体处理器不可能具备的特色(具有解码后微指令快取记忆体的特殊案例,不在讨论範围)。
但如同硬币的正反两面,这也让 Transmeta 的处理器,执行混合大量不同应用程式的效能基準工具(就是我们常讲的 Benchmark)时,CMS 的转译工作将备多力分,会特别吃亏,这也是 Transmeta 难以摆脱的原罪。
软体手段弹性无限大,所以 CMS 可施展各式各样怪招,一点一滴榨出极限效能。
透过软体达成的非循序执行
笔者大学时代在计算机中心打工兼任 News 伺服器管理员前,研究如何架设 nntp 时,在某篇鉅细靡遗的 BBS 文章开头,看到一句「你最好先準备一杯热茶或咖啡,我相信你一定需要它」,大概就是笔者当下对各位读者的良心建议,虽然下面的内容远不如 nntp 艰难,请安心服用。
以下是 4 个 x86 指令组成的执行序列。
转译成 RISC 型态的「载入回存」(Load and Store)底层硬体指令码,先将记忆体载入至暂存器,再进行运算。运算元格式也从 x86 的双运算元(A+B=A),转为 RISC 风格的三运算元(A+B=C)。
为了方便说明,里面沿用 x86 原始暂存器名称,3 个运算指令的预设「预测执行条件码」(.c)在这里不必要,可忽略不看。
接着最佳化,既然 r30 和 r31 都载入相同的资料,那就没必要做两次,让第二个加法指令沿用 r30 暂存器,减少暂存器使用量。
重新排序指令,把同时读取 r30 暂存器的两个加法指令往后排,先完成耗时的堆叠载入 r30 暂存器。不这样做,第一个指令包的两个加法指令,可能就会因等待堆叠资料载入 r30,导致管线停滞。
反应回 x86 指令序列,形同最后一个减法指令,被搬到最前面。
上期文章有提及的 Crusoe VLIW 指令编排方式和指令格式如下。因为是 VLIW,没被 CMS 填入的「原子」就只能填入什幺都不做的 NOP(No-Operation)了。
再把 5 个「原子」(Atom)打包成两个 VLIW「分子」(Molecule)指令包,一个萝蔔一个坑的对应 4 个执行单元,透过软体手段实现非循序指令执行,毋需複杂的硬体实作方式。
发生中断例外,亦可快速回复到先前的执行状态
前面的非循序指令执行看起来很厉害,却也造成新问题:一旦发生中断例外,该怎幺确保符合 x86 指令集的语意规範。
x86 指令集採取「精确中断」(Precise Interrupt),若处理器发生中断(如外部 I/O 发出需求)或例外(像发生分页错误),在「标定下一个被执行指令的记忆体位址」的程式计数器(Program Counter,PC)前面的所有指令,都将执行完毕,后面的指令则不可执行。
我们回到前面的 x86 指令序列。
假如第三个指令造成分页错误(Page Fault,软体试图存取已对映在虚拟位址空间中,但是目前并未被载入在实体记忆体中的分页),第四个指令根本就不该被执行,但经过 CMS 转译最佳化,已经被「先斩后奏」,这就不是纯软体手段即可轻鬆处理的大麻烦了。
因此 Transmeta 在既有的工作用(Working)暂存器档案外,预备了透过 CMS 软体控制的「影子(Shadow)暂存器」,也就是专利权的「Official Register」,存放「实际 x86 执行状态」,等同「指令转译过程中,从头到尾都没发生任何例外,确定没有问题,可被交付(Commit)版本」的暂存器资料。一出乱子,就用影子暂存器的「备份档案」,将工作暂存器的内容整批回滚(Rollback)到先前的状态,再重新转译 x86 指令并执行。
换言之,Crusoe 的暂存器总量非常惊人:112 整数(64 工作+48 影子)与 48 浮点(32 工作+16 影子)。
当发生「真正 x86 状态的中断例外」,关卡式记忆体回存缓冲区(Gated Store Buffer)仅清除尚未确认交付(Uncommitted)指令的资料,已交付(Committed)者仍依序写回记忆体,无需整个砍掉重练。对 1998 年那份专利权还保有印象的话,想必不陌生。
可亡羊补牢的记忆体预测载入机制
这就是 1998 年那份 Transmeta 专利权名称的主角,可见举足轻重的重要性。无论任何处理器架构体系,尽其所能设法「提前」从记忆体载入资料到暂存器,早已是兵家必争的关键性技术。
2006 年,英特尔的「救世主」Merom 微架构,就靠「记忆体位址相依性消歧」(Memory Disambiguation)这招,一举扭转 x86 处理器对 AMD K8 的性能劣势。但平心而论,任何形式的「预测执行」,无不是潜在的资讯安全漏洞。我们也只能悲观预期,英特尔和 AMD 的近代 x86 处理器,安全性臭虫恐怕是永远修不完了。
身为近代多工作业系统技术基础的虚拟记忆体,把位址空间重新定义为「连续的虚拟记忆体位址」,藉此「欺骗」程式,使它们以为自己正在使用一大块「连续」位址。而实际上,通常是分隔成多个实体记忆体碎片,还有部分暂时储存在外部磁碟机,需要时资料交换(Swap)。说到虚拟记忆体的分页表(Page Table),Crusoe 处理器内建转译位元缓冲区(T-Bit Buffer),追蹤需被 CMS 重新转译的分页。
作业系统与应用程式眼中的「虚拟记忆体位址」和实际的「实体记忆体位址」,之间的对应关係不见得是一对一,往往虚拟记忆体空间远数倍于实体记忆体容量,有可能产生两个不同的虚拟位址、却指向相同实体位址的「记忆体别名」(Alias)问题。
载入指令会从快取记忆体或主记忆体读取资料,通常比较耗时,对效能的负面影响也比回存大的多,应尽早处理,CMS 可赌博性的「假设」载入(Load)的实体记忆体位址,和前面的回存(Store)彼此互不冲突(后面载入暂存器的记忆体资料,有可能是前面回存记忆体的资料),提前执行载入指令。
这里先假定 [%x] 和 [%y] 的实体记忆体位址不同,CMS 交换指令顺序。
但毕竟依然存在实体位址冲突的风险,Transmeta 为此追加专用硬体功能单元,协助侦测记忆体别名。一旦将载入移动到回存之前,CMS 也须更替为特殊版本的保护式载入(ldp,Load-and-Protect,额外记录实体位址与资料载入量)与附带检查的回存(stam,Store-after-Alias-Mask,追加安全区域检查)指令,提醒处理器「这是有风险的指令排序,须启动相对应的保护机制」,当不幸「中奖」触发例外后,亦可重新来过。
如果各位一头雾水,可参考英特尔 IA-64 的「资料预测载入机制」(Speculative Load)。编译器将载入指令(ld)搬移到前面,换成专用版本(ld.a)。
透过检查指令(ld.c或chk.a)比对「预先载入位址表」(ALAT,Advanced Load Address Table)记录的记忆体位址、资料区块长度、目标暂存器与资料载入状态。如确定发生冲突,就重新执行回复码(Recovery Code),以确保载入资料的正确性。
这技术亦可应用在削减指令数量。假设先 [%x] 和 [%y] 没有对冲,[%x] 将不会 [%y] 更动,就不必重複载入 [%x] 了,减少指令数和暂存器消耗,并缩短浪费在载入记忆体资料的时间。
两边一起执行,再挑选正确的结果
条件分支(Conditional Branch)一向是处理器管线化的杀手,尤其对 VLIW 处理器,分支造成的伤害特别严重,一卡住就是一整包指令塞车,极力减少分支数量,以提高管线效率,实乃重中之重。
身为他山之石的俄国人 Elbrus 2K 处理器,可选择性分支预测执行,不必猜测到底是那条路径,反正两条指令流统统跑下去,确定条件结果,再保留正确的那一边。与 Elbrus 略有渊源的 Transmeta 亦不遑多让,耗尽极为贫弱的执行单元,换取管线一路畅通,亦在所不惜。
这段由 20 个 x86 指令组成的执行序列,是 Transmeta 官方技术白皮书的「集大成」範例,用上所有前述技巧,将其转换成 10 个 VLIW「分子」指令包。
但各位别被这串「有字天书」吓傻了,这里面最值得注意的是,原本 x86 指令串的两个内部条件分支,换成「从两个结果中挑出一个」的路径选择(Select)指令,两条指令流都转译并执行,变相彻底消除条件分支,利于指令管线化,即使代价高昂。
抄袭 IBM 的嫌疑,依旧挥之不去
在历史上,我们经常可看到,来自不同地方的技术研发团队,在某个特定「历史节点」,採取殊途同归的方向,如 1960 年代因微码而蓬勃发展的 CISC、1980 年代因反动而突然崛起的 RISC,或 1990 年代前期想逃避複杂硬体的 VLIW 风潮。
英特尔 Pentium Pro 总工程师之一的 Robert Colwell,在回忆录《The Pentium Chronicles》就对当时外界盛传英特尔抄袭 Alpha 处理器、英特尔高层还愿意花钱消灾,付大笔专利金给 DEC 这件事深感不满,直言「像非循序指令执行(Tomasulo 演算法)的概念与应用,早在 1967 年就存在了,并不是全新产物」。
同样的情境也发生在 Transmeta。身为 IBM 在 1986 年 VLIW 处理器研究计画的分支,在 1990 年代初期开始的 DAISY(Dynamically Architected Instruction Set from Yorktown,英文简写的意思是「雏菊」)动态编译器技术,在 2000 年 10 月正式开源──刚刚好就是 IBM 宣布取消 Crusoe 处理器 ThinkPad 的时刻──接着在某些技术社群就引起轩然大波。
DAISY 与后继的 BOA(The Binary-translation Optimized Architecture),将 PowerPC 指令码转译成另一种 VLIW 处理器指令集,除此之外,Transmeta 的诸多技术特点,「似乎」在 DAISY 统统看得到,简直是同一个模子刻出来的,唯一的明显差别,只有把 x86 换成 PowerPC 而已。在 Transmeta 还存活的几年内,不少国外技术社群网站的讨论区,充满了信誓旦旦「Transmeta 剽窃 IBM」的指控。
同场加映:在 2004 年,也是 Linus Torvalds 离开 Transmeta 之后,也有人试图证实 Linux 作业系统抄袭 Minix(作者是大名鼎鼎的 Andrew Tanenbaum,资讯科班学生很难不读到他的着作),但 Andrew Tanenbaum 本人很大方的否认了。
事隔多年,直到俄国人真的做出来 Elbrus 2K 处理器和相似的动态转译技术,才让人恍然大悟:这些东西的技术源头,都可追溯于 1980 年代,而且还不见得都出自美国人之手。看在 Transmeta 创办人 David Ditzel 和那票俄国人打过交道的份上,Transmeta 受 Elbrus 团队的影响,搞不好还比 IBM 稍微大一点。
况且 IBM 的研究目的和 Transmeta 截然不同,IBM 想透过软体模拟,让 Power 取代 S/360 体系 CISC 大型主机这件事,在业界早就不是祕密了。不限于 PowerPC,S/390 和 Java 也是 DAISY/BOA 计画的相容对象。
此外,IBM 着重在指令平行化,光转译器就会啃掉超过 100MB 的记忆体容量,Transmeta 则是专注于电源效率,CMS 大不了就 32MB 摆平。硬说这是抄袭,实在有点牵强。但 IBM 曾公开表示「我们有注意到两边很像」,他们私下怎幺看待 Transmeta,外人不得而知,但可以肯定的是,IBM 绝对很清楚 Transmeta 的底细和能耐,也许这也是让 IBM 放弃 Crusoe 的原因之一。
千万不要以为 IBM 公司这幺大,负责 ThinkPad 研发的日本人,不会知道地球另一端部门的观点和看法,只要出现内部询问,不甘寂寞的研究部门,怎幺可能放弃彰显自身价值、秀出「存在感」的大好机会?当然,我们也有充分理由相信,英特尔的「道德劝说」和威胁利诱,才是最重要的主因。
总而言之,只能说 Linus Torvalds 这个人名气太大,加上 2000 年 11 月那场轰动股票市场的 IPO 大戏,让 Transmeta 树大招风。不过,挥之不去的抄袭谣言并不致命,对 Transmeta 和客户来说,最重要的课题,莫过于以 CMS 为灵魂的 x86 处理器,能否落实 Transmeta 当初承诺的愿景。更重要的是:能让公司和投资人「发大财」。
但初代 Crusoe 上市后接连数年的市场发展历程,血淋淋证明 Transmeta 的「网际网路世代软体思维」,终究敌不过摩尔定律预言的电晶体数量成长曲线,再多「技术天才」,也无力抵抗英特尔和 AMD 的「研发大军」,这真的是时代的悲剧。