百科问答小站 logo
百科问答小站 font logo



随着 TypeScript 继续普及,会不会出现直接跑 TypeScript 的运行时? 第1页

  

user avatar   doodlewind 网友的相关建议: 
      

打开 @wryjdhej 回答中微软研究院搞 Static TypeScript 的论文后,真让我吃了一惊,因为它恰好和我自己的业余玩票方向有不小的关系。我尝试认真解答一下。

首先,Static TypeScript (STS) 是什么呢?

STS 是用 TypeScript 写的编译器,能把 TS 的子集编译到 ARM 机器码

难道编译出机器指令的编译器,可以不用底层语言写出来吗?其实编译也就是种「把一种结构化数据转成另一种」的工作,主流编程语言都可以完成。相关的科普介绍请移步我的这个回答: 程序语言都是怎么发明的?

既然是研究院而非微软本家出品,那么 STS 是个玩具项目吗?是,也不是。STS 已经落地到了微软的 MakeCode 项目里,主打面向教育的游戏编程,在发达国家已有百万级的师生使用。像下图中网页里给小朋友们写的,就是 STS:

我都给方老师想好广告词了——现在六年级小朋友都在学 TS,你还敢不学 TS 吗?快来报我的培训班吧!

那么,为什么不用 VSCode 这样自家非常成熟的 TS 编辑器,而要重新发明轮子呢?论文中给了一些这个领域里重要的需求点:

  • 小朋友们需要卡哇伊的友好界面。
  • 小朋友们写的游戏,需要能让小朋友们自己装到配套的游戏机里玩。这些游戏机通常使用 ARM 微处理器,往往只有 16KB 到 256KB 的内存。
  • 学校的电脑一般都比较矬,管得也比较严,Native App 不好装。

万恶的资本主义教育,居然给小朋友们发游戏机毒害他们!这些游戏机长什么样呢?是这样的:

理论上,只要把 JS 脚本拷进去解释执行,不就可以了吗?这有两个难点:

  1. 主流的 V8 引擎非常吃内存,没法放到这上面。
  2. 嵌入式的 JS 引擎往往比 V8 慢几个数量级,容易卡顿,也影响续航。

STS 就是解决这个问题的一种方案——有趣的是,这不是唯一的方案。我业余其实也在做些把 Web 技术栈移植到游戏机的工作。目测去年论文发表时,Fabrice Bellard 大神还没有推出他的 QuickJS 嵌入式 JS 引擎,因此论文里只提到了 DukTape 和 IoT.js 这些老轮子。新的 QuickJS 比这些嵌入式 JS 引擎都快(但也不是差上数量级的快),并完整支持了 ES2019。以此为契机,我近期也基于它做了些嵌入式设备上的引擎移植尝试,比如这两篇文章:

第一篇文章里我用的是顶配树莓派四代,性能算是非常过剩的,不必多提。第二篇文章中,我移植的对象是 32MB 内存的国产掌机 Miyoo,它跑起 QuickJS 的效果像这样:

当我把运行 JS 的目标定在 32MB 内存的机器时,微软考虑的是怎么在 32KB 内存的芯片上跑 JS……好吧虽然感觉他们的目标太苛刻了,但还是不得不服气,是在下输了。

所以,微软这帮人做了什么呢?MakeCode 是面向用户的产品,整套东西做得很接地气,分层设计看起来也很合理。主要包括这些:

  • 完全基于 TS 实现的工具链,可以把 STS 源码编译到 ARM 机器码。
  • 用 STS 写的游戏引擎基础库,包含物理、精灵、输入输出等子系统。
  • 用 STS 写的游戏框架,用来方便小朋友们写平台跳跃、角色扮演等类型游戏。
  • 基于 C++ 预编译好的 STS 运行时。
  • Web 编辑器,其实就是拿自家 Monaco 简单改的。

下面就按论文里的方式,分几个维度介绍下 STS 吧。

亮点

首先,STS 自然是具备过人之处的,否则也不必写成论文了。STS 的独创能力主要包括以下这些:

  • 能够直接将编译 STS 脚本编译为 ARM Thumb 机器码,不像 Babel 和最早的 C++ 编译器那样做的只是转译。
  • 具备和 V8 差距在一个数量级以内的性能,且内存占用低几个数量级。比起嵌入式 JS 解释器,则能快一两个数量级。
  • 在浏览器和命令行里都能完成编译。可以在浏览器里把编译出的二进制文件 Download 下来,复制到机器上就能运行。当然未来可能还有 WebUSB 呢。

限制

STS 既然是 TS 的子集,那么肯定存在一些能力上的限制,主要包括这些:

  • 特性上没有 JS 灵活,舍弃了 with / eval / class 之外的 this / arguments / apply 这些动态特性,丢掉这些好像问题也不大?我就说了 this 就该跟 class 一起用,在其他地方出现都是糟粕,不知道为什么总有人喜欢跟我争呢…
  • 类型系统也没有 TS 灵活。例如当类型 T 具备字段 f,但动态类型 x 不是 T 的 Nominal Subtype 时,执行 (x as T).f 就会报错。除了 Map 类型外,目前也不能为对象动态添加新字段。
  • 只支持用 TS 中的 namespace 来隔离作用域,各个源码文件没有独立的 scope,也不支持 JS module。STS 里把一些文件组合在一起的概念叫做 package,现在的 MakeCode 编辑器就由多个 STS package 组成。package 只是一组文件的集合,你还可以随意扩展已有 namespace 里的 API…感觉有些过于松散了啊。

编译器与运行时

STS 最底层的轮子分两部分:工具链(编译器、链接器等)和运行时

对编译器来说,其职责在于产生 IR 中间表达。STS 编译器的 IR 能够转换到如下三种后端:

  • 浏览器中的 JS,这样可以直接在网页里跑起设备的 Simulator 模拟器。
  • 32 位的 ARM Thumb 机器码,这时内部符号已经链接到了预编译的 C++ Runtime,可以直接跑在 bare metal 裸机硬件上,或作为操作系统的原生可执行文件运行。
  • 配套 VM 解释器的字节码,以便于在 iOS 这类禁止动态代码生成的平台上运行。

在 STS 中,ARM Thumb 和自定义的字节码,都是基于相同的汇编码生成的。这里的亮点在于,这一整条工具链,从 TypeScript 编译器,到 STS 代码生成器,再到汇编器和链接器,都是用 TypeScript 编写的,可以顺利地横跨浏览器和命令行环境来运行。很难想象,类似安卓 NDK 级别的传统嵌入式工具链,居然可以完全用 Web 技术栈来实现

虽然 STS 能生成 ARM 机器码,但这是需要运行时才能执行的。运行时是用 C++ 预编译好的,带有一个基础的垃圾回收器,还默认 include 了些内置的 package。预编译好的运行时是个 ELF 格式的可执行文件,STS 会从运行时的二进制文件中提取函数符号的地址,稍微修改一下编译出的机器码,然后把文件连在一起,从而完成链接。这部分知识在《程序员的自我修养:链接、装载与库》里有详细介绍,可惜我还没啃完…

还有重要的一点,那就是如何实现 JS 中的对象呢?STS 直接利用了 C++ 对象来表达 JS 对象。C++ 里使用 Virtual Table 来实现 OO 中的多态。对每个 class 的实例,都有个装着函数指针的表,指向所有的虚方法。而指向这张表的指针则是所有相应对象中的一个成员变量。调用虚函数时,实际上就是通过查表的方式来找到方法并执行的。STS 中的类使用单继承与静态的内存布局。每个对象都与一张虚函数表和各类字段相关联。

论文还顺便介绍了一些其它用到的技术:

  • 如何表示内建对象,包括 Array / String / Closure / Dynamic Maps 这几种。
  • 如何在代码生成后做一些经典的优化。
  • 如何设计垃圾回收器,用的还是 mark-and-sweep,比引用计数强。

性能评估

性能测试自然也是少不了的,请看下图:

在分析上面这些 micro benchmark 数据前,先做些基本的科普:

  • Binary 测试内容是不停地分配并销毁二叉树,考验内存管理性能
  • Richards 测试内容是模拟系统的队列调度,考验对象属性访问性能
  • Fann 测试内容是计算特殊的排列组合数量,考验整数运算性能
  • N-body 测试内容是模拟计算天文学中的多体问题,考验浮点运算性能

这些跑分数据的要点,也很容易解释清楚:

  • STS 性能在数量级上远超传统解释器
  • 小数据量的时候 STS 甚至能比 Node 更快
  • 处理大量数据时,Node 明显更扛得住

对于具体的性能指标,论文中有很多讨论,大家可以自己去看看设计者自己的评估,还是有不少干货的。

总体来看,语言 Runtime 的性能鄙视链是这样的:弱类型无 JIT (QuickJS) < 强类型无 JIT (STS) < 弱类型带 JIT (V8) < 强类型带 JIT (JVM)。那么如果更进一步,给 STS 加上 JIT 支持会怎么样呢?这就要问微软爸爸了。

总结

STS 的方案充分挖掘出了硬件潜力,只要所有游戏引擎、框架、逻辑等上层代码均可控,那么几乎就能把运行 JS 系语言的硬件配置降低到最低。当然,STS 的设计目标还是和 QuickJS 不同的。后者我已经试过可以一行不改地运行起工业级的 React,而 STS 已经明确了不会支持 JS 了。对于 16KB 内存的极限情况来说,STS 确实是非常牛逼的方案。不过到了 32MB 的量级,差异就没有那么明显了。要知道索尼 PSP 的内存也就只有 32MB 呢。

那么,STS 会成为社区欢迎的新引擎吗?感觉还真不好说。毕竟一言不合就重写经典框架,不是前端社区的 传 统 艺 能 吗?

最后是我自己的几点感想:

  • 现在 JS 引擎领域还有机会大量产生开创性成果的方向,明显已经是这种面向 Niche 细分领域的产品了。建议大家听知乎大 V 吹水 V8 之余,也多看看引领技术潮流的公司们在做什么吧。
  • 相比于 WASM 这样把浏览器外技术「引进来」的战略,STS 和 QuickJS 选择的是让 Web 技术栈「走出去」的路线,把 JavaScript 的革命红旗插遍每一个角落。正所谓试看将来的环球,必是赤旗的世界(误)
  • 今天 Web 技术栈的影响力,已经吸引了越来越多像微软研究院和 Fabrice Bellard 这样的高端玩家入场。以后大家看到什么更天马行空的架构设计也不要惊讶。
  • 嵌入式前端领域还属于蓝海。Web 技术栈跟嵌入式的结合,在教育领域有很大想象空间。如果广大农村甚至贫困山区的孩子能靠着廉价的嵌入式游戏机,用 JS 自己写游戏学会编程,最后改变命运,那真是功德无量啊。
  • 现在学前端都得靠读论文才行了,以后大厂招人要求不会是要几篇 paper 一作吧(害怕)
  • 我万一被裁了,谁能帮我推下简历去微软交个朋友……
勘误,这个项目是微软研究院在 Redmond 的组做的,最早我以为是 MSRA 的项目啦

user avatar   xin-2050 网友的相关建议: 
      

个人觉得,完全有必要。

一个原因就是性能。

没记错的话,ie时代,js性能很差的。

后来webkit内核的浏览器取代了ie,统一了市场。原因就在于webkit内核优化了js的性能。各种预编译,缓存,js性能得到极大的提高。

但是据说,js性能优化已经到了极限。原因就在于js是一种动态类型的语言。一个变量的类型不确定,可以动态变化。但是,机器语言就是cpu认的底层指令是有类型的。所以无法继续提升js性能了。

所以,如果浏览器支持ts直接编译运行,那js性能可以直逼Java和c#。




  

相关话题

  如果说 Rust/WASM 会逐渐蚕食 JavaScript 生态,那将以怎样的路径进行? 
  请问一下,跨平台解决方案中,Qt 和 Electron 孰优孰劣? 
  Node.js 未来会超越 Java 吗? 
  为什么Python不能出一个类似V8的编译器? 
  为什么JavaScript不能成为自己的标准,非得冒出ES6成为JavaScript的标准呢? 
  只会nodejs,但公司强制要求上php,是让他们招个phper,还是自己去学? 
  JavaScript 最失败的设计是 arguments 吗? 
  只会nodejs,但公司强制要求上php,是让他们招个phper,还是自己去学? 
  为什么NodeJS领域那么多框架,而Java则可以SpringBoot一家独大? 
  Node.js、Scala、Clojure 等声称适合高并发的语言,分别具体适用于什么情景,何种任务? 

前一个讨论
人类的渴觉中枢在哪里?如果是在丘脑或者大脑,那么为何一喝水立马就不渴了?
下一个讨论
如何评价数学家、现代概率论的创始人柯尔莫哥洛夫?





© 2024-11-21 - tinynew.org. All Rights Reserved.
© 2024-11-21 - tinynew.org. 保留所有权利