
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指令串行,形同最后一个减法指令,被搬到最前面。

上期文章有提及的CrusoeVLIW指令编排方式和指令格式如下。因为是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处理器,安全性bug恐怕是永远修不完了。

身为近代多任务操作系统技术基础的虚拟内存,把地址空间重新定义为“连续的虚拟内存地址”,借此“欺骗”程序,使它们以为自己正在使用一大块“连续”地址。而实际上,通常是分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘机,需要时资料交换(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的“研发大军”,这真的是时代的悲剧。
(首图来源:pixabay)