• 😓 看了眼地址,变成知乎的 CDN 了,还是看不了。CDN 图片有缓存,自己上传的时候看过的当然能看到,但盗链到其它网站别人看到的全部都是无权限啊。

  • 「ミファーの祈り、いつでも使えるよ」

  • 感觉看了半天大多都是套路云的问题啊,如果是墙的升级的话不该是这种情况吧。

  • 按道理来说 Ruby China 服务器在香港,也没有见过严重丢包。海外链路有丢包其实是挺常见,最常见的问题都和链路质量有关,特别是 peer 的情况。像是 Linode 的几个服务器到中国的 peer 质量本身就很差,有一定丢包量对 TCP 连接其实影响还挺大的,确实对 HTTPS 的影响会更大。不过也有类似于 TCP-BBR 的解决方案。但单针对 HTTPS 进行丢包的实在是很少见。

  • 为啥境外 https 会严重丢包?而且 http 直接被 mitm 才是帮了墙一把吧。

  • RubyMotion 使用的关键就是。。。必须先通过 meta-programming 把这堆东西全部封装一遍,然后这世界就清净了。否则,真的辣眼睛。

  • JSON 解析出错 at 2018年05月11日

    这。。。明显不是一个 JSON 字符串,引号和键值对中间的冒号都是错的。

    如果要把 Ruby Object 转换成 JSON,需要 JSON.generate(object) 或者 object.to_json。直接传 object 会转成 String,但那个 String 并不是一个合法的 JSON 串。

  • 2015 年來的時候還不是因為參加 RubyConf 來的,是來旅遊順道去台大聽了幾場演講,打了一場 Hackathon。當時知道 xdite 還不是從 Rubyist 口中得知,感覺其實當時已經有不少人都知道這些事了。

  • Ruby 2.6.0-preview1 已发布 at 2018年02月24日

    喷了,这个笔误有点太傻了。。。🤦‍♀️

  • ...你需要先搞清 LTS、rolling distribution 和 half rolling 各自都在干什么,以及为什么需要这么干。LTS 是不会也不能引发 Kernel 的 API 变化的,如果引发了,那么这个发行版基本就完蛋了。

  • 其实我很好奇的是,我看你们研究不同的 IO lib 跟玩玩具一样的。。。为什么不直接用系统 API?

    你能正确封装各个操作系统 API 一起用那也算是大几千个 Star 的项目了,操作系统级别的 API 哪里那么简单。从 libev libevent 到什么 Java 里的 NIO 或者 Rust 里的 tokio 做的都是这件事。你以为看着文档写个 kqueue 就能用了?不讲别的,你先搞清 kqueue 在 Darwin 内核里和 OpenBSD 内核里有哪些区别,MacOS 和 iOS 的各个不同版本 kqueue 的行为是否一致就可以搞好几天了。随随便便就说调用操作系统 API 才叫玩玩具。

    像 libevent 里对于 macOS kqueue 上的 bug 从 16 年讨论到 18 年了,有好几个其他 lib 都是假设 mac 不会被用作服务器而忽略这个 bug 的。再比如 midori 中对于 macOS 还有一个 patch

    class TCPServer
      def tcp_fast_open
        opt = (/darwin/ =~ RUBY_PLATFORM) ? 1 : 5
        self.setsockopt(6, Socket::TCP_FASTOPEN, opt)
      end
    end
    

    一个调用在 Linux 和 BSD 下是什么样的。BSD 是这样,fork 自 BSD 的 Darwin 是不是还这样?这些都是要非常小心测试的。不是写了能运行就代表能用。也不是我能用,大家都能用这么粗暴的关系。

    软件是一个系统工程,很多问题,特别是对于缺乏出问题经验的人会想当然被认为简单。只有出过几次问题,最好这几次问题赔了很多钱,自己才会开始谨慎。

    我就说写个 Hello World 好了,你看看是不是在 docker 的 log 里你的 Hello World 程序不 return 掉,log 都不会正确打印?你这时候要返回去看 printf 到底干了啥。STDOUT 是啥?怎么还操作 I/O 了?怎么还有 buffer 了?buffer 在什么情况下会 flush?docker 的 STDOUT buffer 长啥样?一圈看下来才发现,原来写个 Hello World 也能出问题,还得回去看标准库和操作系统的实现。

    软件就是这么复杂,不是想当然的想用什么用什么,想写什么写什么的。

  • 服务器都是开辟/派遣一个 thread 你的一次 action 的

    谁告诉你的?你要不看看 Node.js 这种连自带原生线程这种东西都不存在大几千 rps 的,或者 nginx 这种单线程下也可以几十万 rps 的,是怎么处理 action 这个问题好不好?这两个都「开辟/派遣」thread 了吗?没有,开销那么大的事情讲究性能谁会去做?

    你倒是反过来理解一下,对于 CPU,几个核就能同时做几件事,Thread 在操作系统层面的本质到底是个什么东西。对于网卡硬件,一次只能读进一个二进制流,多路的本质是什么?讲不清这两个问题你才会陷入对多路复用的理解仅限于 Thread 的解决方案。

    读了一下 Thread::Green 的代码,其实 FD 通信,本身就是 blocking 的。

    求求你把 iom.h 重新读一遍,谢谢。

    比如 select 就是个阻塞调用

    。。。一个 multiplexing 的东西要是变成阻塞调用了,怕不是各个操作系统内核都要气死。select, epoll_wait, kevent 本质上做的是一个事情,而他们实现方法不同,导致了效率的区别。你在 Thread::Green 里看到的 select 也不是真正的 select,而是在 iom.h 里对 select epoll 和 kqueue 进行统一封装后的 API。

    服务器的 Ruby I/O 的问题个人认为是 Ruby String 类 效率问题

    你随便怎么 profile,能 profile 出这个结果也是算你牛逼的。

  • JIT for MRI 开始开发了 at 2018年02月12日

    「让一个性能很好的 Thin 拖得只剩下跟其它一样的水平」少开玩笑了,Thin 的优化可以说是非常差了,和其他 Web 服务器比起来最大的区别就是把 I/O 包装成了异步。

    puma 只处理 Web 服务器的 I/O,应用层调用 DB 也是 I/O,其背后的调用逻辑和

    Thread.new { // 数据库 -> @data }
    

    还真的区别不大,Rails 的实现就是类似于 GCD 一样做了个线程池,很明显这个方法是问题的根源。

    你确定 View 能渲染出 @data 的内容,你确定渲染的时候 数据库已经读完了?

    当然是可以的,但是这里就不是依靠 Thread 或者什么 join 来解决的问题。这就是并发和并行最大的区别,吞吐量和请求处理时间也不是一个概念。你试试用 em-synchrony 替换掉这个部分,就会得到数倍的性能改善。em-synchrony 也是纯 Ruby 实现的,按理说,往里面加一层东西应该会更慢才对,为什么反而快了好几倍呢?

    你对并发和并行的理解太混淆了,以及对 I/O 是什么理解很有问题。View 层和 DB 层都不是在 Ruby 里面处理的,为什么同样调用 mysql2 的驱动,用 goroutine 和用 Ruby 性能差别这么多,问题就出在这里,调用的方法不对。而不是什么别的。

  • JIT for MRI 开始开发了 at 2018年02月12日

    我期待它能本身提供编译的功能,就是 ruby file.rb 就已经能从 YARV -> 机器码,而不依赖外部工具。

    这不叫 JIT 这叫 AOT。尝试的话 RubyMotion 的 Ruby 就是 AOT 编译的。这些尝试 Ruby 社区都有做过,优缺点也都是很明显的。

    rails 太臃肿了,I/O 开销实际上是很小的一部分... 而你的 HTTP parsing、业务处理才是最大的问题。

    我们拿比 Rails 轻量很多的 Sinatra 和 mysql2 裸驱动,不用 ActiveRecord 出来跑个 benchmark 好了。

    Web Server Basic View View + DB
    WEBrick 273 req/s 116 req/s 111 req/s
    Thin 1597 req/s 174 req/s 139 req/s
    Unicorn 605 req/s 121 req/s 121 req/s

    为什么 Thin 在 Basic 的时候碾压其他 Server,因为它 I/O 异步了。为什么 View 和 DB 又变差了呢?因为读取模板和查询数据库是同步的。我写 midori 的时候,把每个请求的栈分配都弄到极轻量,还用了 C extension 做 HTTP Parser,单跑 Hello World 的时候也就比 Thin 再快一倍,而加任何一个 I/O 操作就慢十倍。

    谁是「很小的一部分」谁是「很大的一部分」就是这么简单的问题,哪来什么刚好相反。但是只关注 I/O 会限制 Ruby 在 Web 服务器以外别的领域的发展。所以 MJIT 还是非常必要搞的,没人讨厌快,但不希望快是以牺牲其他东西为代价,比如 Rubinius 把代码写到自己也维护不动。而 MJIT 所做的就是这一点。

  • 其实在我做那个试验时候,你的 em-midori 还没加 C HTTP parser 进去,后来我看到你加进去了。

    midori 的 HTTP parser 用 C ext 是 2017 年 1 月 6 日随着 commit 3438d00 加入的,你注册 Ruby China 是 2017 年 6 月 10 日。

    Ruby parsing 效率很低

    快不快慢不慢 profile 一下就知道,midori 换完 parser 对性能影响 < 10%。

    我的观点是,Ruby 与其发明这些 IO 优化的玩意,不如解决计算上优化。

    IO 是有瓶颈的,就算你解决了并发,非阻塞,实际上计算性能还是差得老远的情况下,你 Ruby 代码连磁盘 I/O 都跑不赢,很可能你刷新一个网页,I/O 就那么点东西,但是逻辑就已经够你处理半天了。

    如果计算是瓶颈,那无法解释 Thin 服务器单线程的 Hello World benchmark 为什么能比 Webrick 快将近 20 倍。Web 服务器本来就是 I/O heavy 而不是计算 heavy 的。你用 Ruby 写个堆排那才是真惨,但写完你发现跑的竟然比 Python 3 还快。可见 Ruby 服务器慢绝不是但但的解释器执行效率低这种问题。再反过来 Ruby 上了 RTL-MJIT 我们按最快的那个版本算,就算快 6 倍,比起 Pypy 还是有很大的性能差距,为什么?

    实际上问题还在后头,你有业务逻辑代码。

    网站的业务逻辑绝不是性能瓶颈,大多数 API 核心就是 CRUD,CRUD 的部分的计算都是依赖数据库在运行。自然很大的问题是等待数据库时的性能,而不是 Ruby 本身运算的性能。

    IO 就那么点东西,但是逻辑就已经够你处理半天了。

    究竟 I/O 是大头,还是逻辑是大头,不是想当然说出来的。是通过跑 profiler,跑 benchmark,跑 gdb lldb 找出来的。高德纳说过:「程序员浪费了大量时间考虑程序非关键部分的速度,但如果算上调试和维护,这些性能企图事实上带来严重负面影响。我们必须忘记微小性能收益,97% 的时候:不成熟的优化是万恶之源。但也别放过优化关键 3% 的机会。」优化性能的关键就是去找这 3%。如果为了计算上的极致优势,那我们都应该写汇编。

    Thread::Green 实现的是不用陷入内核态的轻量级调度,解决的是 类似于 Promise,RxJava 那种异步同步的问题吧。这样的话,跟 GCD 也没什么区别,GCD 很大一部分用例就是把线程异步回调同步起来。

    无论 Promise,RxJava 还是 Thread::Green 和 GCD 解决的问题都有很大的区别。这里面只有 GCD 是「把线程异步回调同步起来」,剩下的都不是。如果为了解决 I/O 异步去用 GCD,那 performance 可以说是惨不忍睹的。GCD 是来做并行计算的同步回调的,而不是解决并发 I/O 的同步回调的。这是完全两个概念。比如你用 GCD 写个 fibonacci 就会有性能提升,剩下几个写 fibonacci 都不会提升。但写个 RPC 则是前几者的性能提升很明显,后者提升不明显。这两个东西不但完全解决的是不同问题,而且从原理和实现上也差的非常多。

    不能因为名字里都有「异步」「回调」「同步」就认为是一个东西。既然你那么喜欢 Cocoa 的 API。那么关于 Web Server 用 GCD 本质是 blocking 的这个问题 IBM Kitura 2 年前就讨论过了。https://github.com/IBM-Swift/Kitura/issues/121。或者看 vapor 去年 11 月前后的 GitHub Issue,社区试图从 libdispatch 转换到 non-blocking I/O 所作的工作。

    对客户端作用大一些吧,对于服务端做同步这种事情。。。你让服务器去做长时间等待异步同步,那是在消耗服务端资源。

    你不让服务器等待异步同步,就要等待阻塞同步,或者线程上下文切换。你可以比较一下到底哪个消耗资源。

  • JIT for MRI 开始开发了 at 2018年02月11日

    JRuby 主要是和 MRI 有诸多不兼容的地方。一方面没有引入类似 JNI 的机制做 C 扩展的引用,另一方面像是 Ruby 这几个版本非常重要的 Fiber 特性,一直不跟进。这对 JRuby 的推广有很大的障碍。但是反过来说,Ruby 社区为了保留灵活的演进,在几次劝进下都拒绝搞标准化。事实上最后搞了一个标准化 Ruby ISO,基于那个实现了 mruby,社区反映也是不温不火,两边都有苦难言啊。

  • 是不矛盾,我就是补充一下我觉得就连 token 这个问题也是要打问号的。

  • 我现在不敢单独说一个区块链项目好坏。牵扯利益是一方面,另一方面是来吵的人通常很暴怒又不知道自己在说什么,但这些都是小问题。

    区块链是解决去中心化问题的一个手段,不代表所有去中心化问题都可以用区块链解决。现在啥玩意都能套区块链,况且区块链已经成熟解决的货币去中心化问题,还有一堆问题讨论了好多年至今都找不到解决方案。

    某某项目做区块链物联网,看了半天也没看出和以太坊做有啥区别。动不动说自己是 OSI 第三第四层的底层协议,明明没有现有 OSI 第三第四层你连设备发现都做不出来。

    动不动说自己是区块链几点零。某某翻了不知道多少倍的项目,做的不是 devcon 上说了很多次的 sharding 吗?会上提到的问题怎么又不解释了呢?

    一堆项目从 17 年憋 ppt,憋到 18 年憋了白皮书,里面要么公式都没,要么公式都在胡扯。翻遍 GitHub 也没找到实现了点啥。但最最大的问题是,靠着同行衬托,做到这些都已经是明星项目了,当然涨个不停,就是可以投资啊。

    我最早接触比特币的时候,丝绸之路还很火。这么多年看了各种白皮书、黄皮书,后来又开始读大项目代码细节。但越看越多,越看越深入,这个疯狂的市场,越来越看不懂。

    每次一说区块链的问题,就有人冲上来:你比中本聪、Vitalik 还牛逼?你想的问题他们想不到?我确实不比他们牛逼,因为他们确实想到了啊。bitcointalk 和以太坊的 Devcon 这些问题都讨论好久了。这些资料 Google 很好搜,会议也是全程录像的。被有些人一问我也怀疑了,到底是我牛逼,还是你们瞎啊。

    在今年 1 月 16 日写了篇骂区块链的文章,当晚就在各种韭菜群里被狂骂。写完第二天区块链各个币都暴跌了,如果这些币当中真的有哪一个超越了比特币,真真正正解决了不得了的问题,为什么大家跌得比比特币还狠呢?

    所以我现在是不想再多说什么了。

    附 1 月 16 日写了如下这篇文章:

    中本聪的野望

    在说这个问题之前我们还是先来理一下区块链是什么。中本聪在白皮书中通过描述区块链的机制,目的是实现一种点对点的电子货币系统。

    简单来说就是设计出了一种账本系统,保证账本系统内的所有人都不能篡改账本的共识。为了实现这一目的,区块链首先被设计成了一个类似于「顺序表」的系统,只能不停往上增加数据,不能在任意地方随意插入数据。其次,引入了 PoW (Proof-of-Work) 机制,其基于哈希 (hash) 这一概念。

    每一个区块的产生需要计算自己所有数据和上一个区块的哈希的哈希,同时这个哈希还必须以多少个 0 开头。由于我们无法反向计算一个以多少 0 开头的哈希数据长什么样,只能纯随机地进行猜测,但我们又很容易验证这个结果。最后一个需要解决的问题是如何鼓励人们去产生下一个区块,也就是挖矿。给那个算出这个结果的人奖励一定的货币,即解决了发行问题,也解决了共识问题。

    于是中本聪基于一个纯数学的概念创建了一个公开、透明、对等的账本系统。再也没有了中央银行,人民的财产由人民自己保护,谁对维护社会做得贡献多,谁就能获得更多的资产。

    按劳分配,完美!

    完美…吗?

    答案是否定的。比特币诞生后的不久就产生了一个违背比特币原则的许多发明:矿场、瘦钱包。

    矿场

    我们先来谈矿场。挖矿之所以保护了所有用户的权益基于的是其博弈的数学本质。然而随着比特币的区块难度上升,单个设备挖出矿的概率很低,虽然一旦挖出就是一笔巨款。为了降低风险,矿工们选择抱团挖矿,这也就诞生了矿场。为了降低挖矿的成本,现在的矿机被设计得非常简单,验证区块合法性这个本身被设计的挖矿目的被某个矿场中心所替代,所有的从机只负责算哈希。放弃义务的后果就是放弃了权力,矿工们今天已经沦为了矿场的利益工具。像比特币现金、比特币钻石、比特币黄金这些一个比一个胡扯的东西被分叉了出来。参与分叉的矿工大多甚至都没有意识到自己的挖矿工具突然参与这次分叉活动。

    比特币不是共产主义,它不会保护产生有钱人。但比特币设计基于的算力博弈,是需要保护不能产生一个算力巨头的。矿场的诞生使得这个设计彻底破灭。

    瘦钱包

    瘦钱包在比特币的白皮书中被简要提到过:「不运行完全节点也可验证支付,用户只需要保存所有的 block header 就可以了。用户虽然不能自己验证交易,但如果能够从区块链的某处找到相符的交易,他就可以知道网络已经认可了这笔交易,而且得到了网络的多少个确认。」

    这里说的是「验证支付」而不是「验证交易」。为了验证交易,我们需要存储了整个区块链的完整节点。在比特币的原始白皮书中,并没有故意去分开钱包和节点。作为一个 P2P 网络他们应该是一体的。但今天我们看到有完整节点钱包的用户凤毛麟角。写稿的时候,全世界共有 11611 个节点,其中中国有 827 个节点。这一数字已连续下降数年。

    维护比特币钱包是困难而昂贵的,节点消耗大量的硬盘、带宽、电,很多用户也没有维护节点的运维能力。如果是用完就关,那更是吸血驴一般。而比特币矿场、交易所们,维护了这些节点。他们从维护节点中获取到了更大的利益。数量过少的节点更可能被政治冲击、技术冲击。

    更何况,P2P 网络依然需要种子连接来启动,这并不是完全的去中心化。如果 DNS Seed 遭到劫持,理论上可以通过修改,手动编译来解决,但现在的区块链用户们,具备这样的专业素质吗?

    这就类似于电驴是 P2P 技术,但我们可以把 VeryCD 端了啊。BT 下载是 P2P 技术,我们先培养迅雷吸血用户,再把迅雷一锅端了,你到哪里去找 Peer 节点去?

    以太坊很忙

    比特币之后,区块链 2.0 的头衔常放在以太坊身上。对于上述的问题,以太坊一个都没有解决。相反,以太坊引入了更多问题。以太坊引入了一个图灵机 EVM,用于执行合约。听起来好像很好,但以太坊则是从空想社会主义踏入了空想共产主义。以太坊引入了 gas 来评估合约运行成本,这是一个极其粗糙的模型。以太坊无法正确评估一个合约的资源消耗,如果你要在运行程序前知道这个程序的运行花费,这等价于图灵停机问题,是理论上的无解。

    加入了图灵机并不代表可以解决所有问题。像 Filecoin 这种在区块链上存储文件的模型,通过 PoSt 证明了存储,但对于流量又无法证明。这使得所谓的万能应用根本无法实现。更不要扯迅雷那种可以自己停机的所谓「区块链项目」了。

    更麻烦的是,gas 同时评估计算、内存和存储,这使得标准非常失衡。对于某些合约定价可能过低,对于另一些则过高。在以太坊上,这种事情的发生是日常。活跃的合约比起一个账本实在是过于复杂了,这使得以太坊的拥塞极易发生且很难预测。

    速度、安全性、规模三者不可兼得,必须舍弃一个。以太坊舍弃了规模,现在某些号称自己是区块链 3.0 的系统则是舍弃了安全性。目前根本没有一个完美的方案来平衡这之间。

    比账本更复杂的合约也造成了很多问题,如果合约本身签得有问题怎么办?对于这个问题,大多数以太坊合约都通过后门的形式允许发布者二次修改合约,而签订者毫无还手之力。这使得以太坊空空增加了这一大堆问题,对于其想要达到的开放、平等的合约系统也没有实现。

    代币邪恶论

    对于这些问题,许多人仍选择视而不见。而代币的狂热更是让投资人的热钱滚滚涌入,与其说是投资,这毋庸置疑是投机。这些投资人白天在会议上吹着区块链技术,晚上自己连区块链的实现原理根本不了解。做互联网的、买房的、养猪的、炒股的、炒期货现货的都在把钱扔进来,有钱不赚当傻子?还有人声称区块链「代币」是邪恶的,但「区块链技术」本身不是,可以有巨大的应用场景。醒醒吧,世界上就没有区块链技术。区块链技术里的每一样东西都是我们学习密码学都见过的。像是给企业内部部署私有链,完全也是无稽之谈,靠简单的公钥私钥就能解决的事情,非要套上区块链的外壳,把事情复杂化。今天说「代币」是邪恶的,但「区块链技术」本身不是的,说到底是资本的既得利益者,他们要么希望让泡沫更高,或是希望借机在其中捞一笔。

    不得不说,今天的区块链和 2008 年的次贷危机的前奏已经非常像了。次贷危机之所以能危机,是因为通过次贷,使得账目表面上没有那么难看,等无法收场时,就已经变成巨大的窟窿了。区块链的今天也是这样,所有的事情都在掩盖代币,通过一次次的 ICO 和新概念,掩盖之前区块链已有的问题。一旦问题爆发,涉及到的资金将是空前的,甚至是远超 2008 次贷危机的。并且区块链的全球化、点对点的特性会让任何政府直接调控的难度变高,将会成为前所未有困难的金融大危机。

    我不希望这一天会发生在 2008 年后 10 年的 2018。

  • 看了下 RubyMine 应该会尊重你对 Hash 构造的换行,只是会调整缩进。试了几次都没有复现这个情况。

    Format 前:

    Format 后:

    我的 RubyMine 版本是 2017.3.2。不知道是不是和版本有关

  • C 线程默认就是绕开 GIL 的,反而是你回调 Ruby 线程才需要主动加锁。这个问题,7 年前就加入对应的 C API 了。Feature #4328

    MJIT 解决的问题和 AutoFiber(Thread::Green) 解决的不是一个问题,对性能都有很大的影响,但影响的地方不一样。一个是计算,一个是 I/O。

    Green Thread 不是 Thread。哪怕在 Ruby 3.0 目前的规划中,跨 Thread 永远是有锁的,Guild 间可以无锁,一个 Thread 可以部署多个 Guild,Guild 间不能共享变量,必须依赖 Guild 通讯。而 Thread::Green 甚至可以是 Guild 之下的,可见和 Thread 没啥关系,更接近于单线程版本的 goroutine。Thread::Green 的加入使得不必使用 Thread 的地方可以不用 Thread,降低上下文切换的成本,这对 Ruby 虚拟机性能影响是很大的。

    和 Cocoa 里的 GCD libdispatch 做的也完全不是一个东西。GCD 是对于线程的封装,是来解决并行问题的,而 Thread::Green 是来解决并发问题的,并行不是并发的唯一解决方案,Thread::Green 也不是并行的方案。

  • JIT for MRI 开始开发了 at 2018年02月11日

    MJIT 这个优化还是要先讨论一下 https://github.com/vnmakarov/ruby/tree/rtl_mjit_branch 这个 branch,在 Ruby Issue 上是 Feature #12589。2017 年 RubyKaigi 上最后一个大演讲就是这个。

    演讲见此:https://www.youtube.com/watch?v=qpZDw-p9yag (各位可能要熟悉一下毛式英语)

    Vladimir Makarov 在 Redhat 里工作,开发 gcc 了 20 年。来优化 Ruby 解释器上手就是一套连招,可以说是相当厉害,一个叫 RTL,一个叫 MJIT。在此之前 JIT 在 MRI 上的尝试也有一些,这里就主要说一下这个实现的不同。

     _______     _________________
    |header |-->| minimized header|
    |_______|   |_________________|
                  |                         MRI building
    --------------|----------------------------------------
                  |                         MRI execution
                  |                                            
     _____________|_____
    |             |     |
    |          ___V__   |  CC      ____________________
    |         |      |----------->| precompiled header |
    |         |      |  |         |____________________|
    |         |      |  |              |
    |         | MJIT |  |              |
    |         |      |  |              |
    |         |      |  |          ____V___  CC  __________
    |         |______|----------->| C code |--->| .so file |
    |                   |         |________|    |__________|
    |                   |                              |
    |                   |                              |
    | MRI machine code  |<-----------------------------
    |___________________|             loading
    
    
    

    首先,MJIT 和 YARV 的实现是解耦的。这一点很重要,Rubinius 支持 JIT 了一段时间又不支持了,很大程度上就是和虚拟机耦合在一起的 JIT 实现会把开发进度拖入泥潭。一方面,Ruby 语言本身还在发展,在 JIT 开发过程中会不会牵制语言。对于对于调试的难度也有很大的上升。而 MJIT 可以很容易开关,类似于外挂在 YARV 上,这是非常好的。

    RTL 指的是 Register Trasfer Language。gcc 的中间语言就是表示为这种形式。vnmakarov 用这玩意作为 Ruby 的中间语言,来替代 YARV 的 ISEQ 设计。RTL 比起基于栈的中间语言设计,执行速度和对内存的消耗都更少,给予更大的优化空间。

    这一套连招打完对于性能的提升可以说是非常恐怖的。我们看一下 benchmark:

    v2 base rtl mjit mjit-cl omr jruby9k jruby9k-d graal-22
    while 1.0 1.11 1.82 387.29 9.28 1.06 2.3 2.89 2.35
    nest-while 1.0 1.11 1.71 4.97 3.97 1.05 1.38 2.58 1.66
    nest-ntimes 1.0 1.02 1.13 2.19 2.43 1.01 0.94 0.97 2.19
    ivread 1.0 1.13 1.31 13.67 9.48 1.13 2.42 2.99 2.33
    ivwrite 1.0 1.18 1.78 15.01 7.59 1.13 2.52 2.93 1.97
    aread 1.0 1.03 1.44 19.69 7.03 0.98 1.79 3.53 2.17
    awrite 1.0 1.09 1.42 13.09 7.45 0.96 2.18 3.74 2.55
    aref 1.0 1.13 1.67 25.73 10.17 1.09 1.87 3.69 3.71
    aset 1.0 1.51 2.68 23.45 17.82 1.47 3.61 4.49 6.33
    const 1.0 1.09 1.53 27.53 10.15 1.05 2.98 3.89 3.01
    const2 1.0 1.12 1.31 26.13 10.06 1.09 3.05 3.81 2.41
    call 1.0 1.14 1.54 5.53 4.75 0.9 2.18 4.99 2.86
    fib 1.0 1.21 1.43 4.16 3.81 1.1 2.17 5.03 2.26
    fannk 1.0 1.05 1.1 1.1 1.1 0.99 1.71 2.32 1.02
    sieve 1.0 1.3 1.72 3.34 3.36 1.27 1.49 2.42 2.02
    mandelbrot 1.0 0.94 1.11 2.08 2.11 1.08 0.96 1.56 2.45
    meteor 1.0 1.24 1.27 1.71 1.71 1.16 0.9 0.92 0.54
    nbody 1.0 1.05 1.14 2.73 3.07 1.26 0.97 2.31 2.14
    norm 1.0 1.13 1.09 2.52 2.49 1.15 0.91 1.45 1.62
    trees 1.0 1.14 1.23 2.3 2.21 1.2 1.41 1.53 0.78
    pent 1.0 1.13 1.24 1.71 1.7 1.13 0.6 0.8 0.33
    red-black 1.0 1.01 0.94 1.3 1.14 0.88 0.98 2.52 1.03
    bench 1.0 1.16 1.18 1.54 1.57 1.15 1.28 2.75 1.81
    GeoMean. 1.0 1.12 1.39 6.18 4.02 1.09 1.59 2.48 1.83

    可以说是脚踩 Graal,吊打 JRuby,3x3 目标立刻实现。三倍是什么?提升六倍也可以啊。

    去年演讲结束后,就有提问在会上劝进 Matz 要不要合并。Matz 的意思是很明确的,说这玩意好应该合并,然后就基本上钦定硬点会后这半年来社区 JIT 的努力方向。

    但这次合并到主干的代码和 RTL-MJIT 还是区别很大的,Issue 为 Feature #14235。首先合并的不是 Vladimir 完整的 fork,而是一个由 Kokubun 拉出来的分支,这个分支叫做 YARV-MJIT。只包括 MJIT 的部分,只在 JIT 中使用 RTL,而没有在 IR 上完全用 RTL。

    这是因为,RTL 版本的 MJIT 要通过回归测试得到合并,短时间内要做的工作太多了。而且 Ruby 还在发展,要 RTL 的开发也完全跟上难度很大。合并的理由主要是 trunk 上很多优化工作和 MJIT 重复了,不要把一件事做两遍。另外就是这个提交的 issue 和 YARV-MJIT 开发主分支上还有一些区别,去掉了一些激进的优化,这使得性能比 YARV-JIT 更慢一点。至少能用了。

    benchmark 大约是比 2.0 提升了 2 倍左右,比完全体的 RTL-MJIT 的 6.18 倍还是慢了不少。但这个合并至少确认了 MRI 的 JIT 开发方向,减少了大家做的许多其他尝试。但这个优化在回归上还是有一些问题,也有一些 bug。即使合并了,也需要在加入特定参数才会开启,2.6.0 里并不会默认启用。

    另外就是标题里这个「开始开发了」,其实不太对,开发了快一年了,这次是要开始合并到 Ruby 主干了。

  • 这么说倒是,应该对 Ruby Thread 做过处理的应该都会调用到这些方法了。

  • 对,方案就是增加这些 hook。但这些 C 扩展还是要手动手动加上 hook 以支持。这几个本质是让同步等待由 Ruby 去监听 fd,等变化了再返回来调用 C。不知道第三方社区的跟进力度会如何了。

  • Linux、Ruby 不冷没天理! at 2018年02月04日

    好久没看到那么有意思的帖子了。

    “我”技术不行,我用工具干嘛得懂怎么修理工具?

    当然自己不会修,受制于人的话,那其实这几个也没啥区别。但是工具出了问题,还是要有人来修的。如果微软家的系统真的可以通过花钱解决一切问题的话,那我也愿意花钱解决,谁没事喜欢自己乱折腾呢。

    2016 年的时候我上报了一个在 Windows 更新后产生的 UAC 权限的严重 bug,提交了大半个月还没 fix。打电话给客服和我说直接重装系统吧。感觉微软家几个客服里也就只有 Azure 的客服专业点。

    后来上微博发了牢骚,又得到了微软微博客服的官方回复。

    最后只好停机 fresh reinstall。

    当然苹果也好不到哪去,有一次给 Safari 的 JavaScript 引擎 Nitro 提交了 bug report,拖了一个大版本,Safari 从 9 升到了 10 才 fix 的。

    Linux 嘛,逼急了至少还有自己修的可能嘛。

    苹果......你知道苹果系统多少? 你知道苹果的手机和笔记本为什么配置这么高?

    微软......你知道微软系统多少?你知道微软的服务器为什么配置这么高?

    完全一致!

  • 引入 ActiveRecord 其实就可以考虑不要上 Sinatra 了,ActiveRecord 还依赖 ActiveSupport,一通下来,已经半个 Rails 都导入进来了。除了写路由的方法发生了变化,总的来说没有区别。特别是加个 WebSocket 还要自己处理集群平衡。如果使用 Redis (Ohm) 做数据库,那感觉还是挺爽的,明显会变快不少。重写一个 ActiveRecord 可以考虑直接上 Sequel 吧,基本满足需求。