• ...你需要先搞清 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。不知道是不是和版本有关