• 既然能被 GC 则不是 static,然后又 final,那就是相当于 “不可改变的动态创建对象”,作为默认值属性用途还是不错的

  • 我的理解就是 Mutable & Immutable Variable,LLVM Style,Rubinius 口味,语义解析中的常量折叠,Java 中的 final。

    To Think(遗留问题):如果 frozen 不被 GC,那么问题可大了,内存占用问题。

  • 其实,这个连接池只是 IO 用的,执行任务(到 rack 层交给 Rails 去 Route 去 Controller.method 是另一边),提升连接池超过 16 之后并不能显著提升并发,反而很多线程空余,虽然不阻塞。然而 Ruby 的线程并不 Parallel,开多了抢占 管道在 IO uds(好像以前是用 pipe)的另一头获取信号的时候卡 shi 你。

    补充: puma 的连接池是轮询的,发现 readable 的 连接 就会再次丢进去 任务队列,通知让其中一个线程抢占到之后执行。其实终归比起条件变量,性能还是差一些。

    然后再加上有 GIL,我也估计是因为 GIL puma 才选择 管道式通知的,因为走条件变量实现方式的路,不如 C 的高效,说了 Ruby 多线程不并行。

    然后很早以前很多人对 Fiber 有误解,以为 Fiber 能取代 Thread 啦,其实 Fiber 并不是解决并行问题的,而是解决异步同步的,跟并发关系大一些,跟 Goroutine Generator(生成器)是差不多的。

    Thread 本来应该并行的,但是不代表 Python Ruby 给它实现并行

  • Ruby 25 岁生日 #ruby25 at 2018年02月24日

    Loving Ruby

    t 站已送出祝福

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

    JIT Yeppy~!

  • 好啦,逃 👣

  • 我写的 cocos - tars 游戏服务项目算是吧,不过日志比玩家信息数据要多,差不多吧,咿呀哈哈哈哈

    其中的一小小部分,其实还有分布式好几个 Server 的 log

    不过用 Cpp 写的,跟 Ruby 无关,在这里发言会不会让众多 Rubyist 有看法

  • PUMA 实现简要分析 at 2018年02月23日

    嗯嗯,不错,像我就会啥都哔哔,咿呀哈哈哈哈

  • 话题绕太远

    回到原来的话题,实际上我本来想表达的就是 Ruby 支持并发,但不并行。

    在 GIL 场景下,你除了写扩展,或者出门左拐 JRuby,在 MRI 传统 rb 编码是分时并发的,它解决的是均衡让多个线程能等分 CPU 时间,实现了一个虚假的 “并行”,但实际上只要 GIL 的存在,它消耗的时间是不会少的。

    但是由于 IO 能绕开 GIL,IO 是不会被 GIL block 的,那是系统层面等待而不是语言平台级别去等待。所以我看大家都在极力证明服务器 IO 密集啦什么的。

    但是我个人观点是对于业务复杂的 site,基本都是 programming complex,造成 computing complex 和 CPU bound,A 类 -> B 类 的一大波业务,还有字符串处理等等,不是小损耗。而系统 IO 基本都是 C 甚至 Assembly Instruction(汇编指令)来完成,实际上提到的那些 Goroutine java.nio,都是为了把【等待】给异步开来,不阻塞原本 Main Thread 的 code 执行。

    研究 IO 的本质就是【模型】,玩来玩去就是设计模式,所以我才会说 “玩玩具一样的,还不如直接调用系统 API”,虽然细看会有差异,还可能调度上的差别产生了性能差别。

    但是,我提及 asio,是我觉得研究这个还不如换 C / C++ / Java 去 implement,因为 Ruby 似乎并不擅长这个,就算写出来了,一个方法调用就已经内部损耗了很多重、一大波的指令。

    所以我才会描述很极端,但又很现实的情况:

    你 Ruby 代码连磁盘 IO 都跑不赢,很可能你刷新一个网页,IO 就那么点东西,但是逻辑就已经够你处理半天了。

    其实 Ruby 简单的异步 IO API 就已经完胜应对 IO 了,puma 能做到如此精简同时高性能。

    与其浪费时间去折磨这些,不如转 C++ / Go / Java,如果深爱 Ruby,不如好好思考一下 GIL 下一步该怎么解决。

  • PUMA 实现简要分析 at 2018年02月23日

    这篇文章不够完整,只写了 IO,实际上它还有协议 parsing,那部分在 ext 目录用 C 写了,这个 parsing 部分 amber-kit 和 midori 这个部分和 puma 都差不多,parse 正确的才算正确的包,错包、不完整的包,还是有相应处理的。

    其实这些协议部分在 mozilla 文档是有相关介绍了,而且还有 how to write a server 相关的 tutorial

  • PUMA 实现简要分析 at 2018年02月23日

    Puma 的实现非常简单优雅。比我想象的简单很多,我应该会尝试下如何结合 Puma 和 LightIO 以取得更好的性能(也因为懒得为 LightIO 单独再写个服务器了)。

    你为什么不告诉我 LightIO 然后我可以把我的 amber-kit 试着使用你的什么鬼 LIO 嘛,这样你可以专心写你的 LIO,我专心写 Amber-kit

    不过,我可能更倾向于使用 system IO API,或者 Thread::Green

  • Docker / Vagrant 完全没问题,现在就在用,不过我更喜欢用 Vagrant,因为我主要在 macOS 上做开发。Windows 的话我也会用 Vagrant。

    反正体验比较流畅,Windows 字体渲染也挺细腻的,有它特有的风味,share 一个文件夹作为 workspace 即可。

    如果作为部署环境的话,Windows Server + IIS + Vagrant / Docker 也行。

    Docker 在 Linux 作为云部署比较好,很多只要支持 Docker 镜像的主机平台都可以 docker image 直接搬。

    我开发一般采用双平台:

    • Windows

      • Ruby Installer - Windows Application
      • Vagrant + rvm + Debian - Main
    • Linux

      • rvm - Main
      • Vagrant / Docker + rvm + Debian - Testing / Production
    • macOS

      • rvm - Main
      • Vagrant + rvm + Debian - Testing / Production

    在 Windows 下面,除了兴趣的 Win64 程序(包括桌面端、控制台)会用 Ruby Installer 之外,其它所有开发都用 Vagrant,作为主要开发环境。

    在 Linux 下,rvm 就够了,Vagrant / Docker 作为测试和生产环境。

    在 macOS 环境和 Linux 情况基本一致。

    我不是来比较 Vagrant 和 Docker 的,不引战。

  • 你不知道 lts 系列是有软件包升级吗?内核会有更新

  • 你说的情况,我也是在 Darwin 碰到过。。。不过不是 IO API。而是 inet 部分的,这个很正常,Linux 内核本身就不是完美的,要不然 ubuntu 就不会有 LTS 长期维护保证持续 fix,macOS 其实升级也会更新 command line 还有 XNU。

    遇到这种问题,每次都找 Core Team report。嘛,嘛,自己 fix 不太现实。

    使用 lib 确实好解决平台问题,比如 boost::asio。自己写是遇见这种问题的。

  • 我不管怎么封装,反正都一个目的,都是把事件异步的系统调用,what about iocp with Win64?

    你在 Thread::Green 里看到的 select 也不是真正的 select,而是在 iom.h 里对 select epoll 和 kqueue 进行统一封装后的 API。

    这是废话,GCD 的 libdispatch 也说自己是 kqueue 封装后的 API……

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

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

    我又不打算混在一起讨论~

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

    其实我的回复只是告诉 #7 其实 rails 并没有被 IO 卡,puma 的异步处理还是很优秀的。

    希望 mjit 能在 rails 这块庞大的 monolith 发挥作用,随着 SPA 开发模型、前端独立岗位的出现,还是亟需的。

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

    等待数据库处理的时间是数据库本身的问题了,不属于 Ruby 管的范围,你们写 Rails 难不成:

    class xxxController
      def xxx
        @data
        Thread.new { // 数据库 -> @data }
      end
    end
    

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

    好,如果你 join 是吧,你可以让这次 puma task processor 的线程等待你执行完毕,但是,这就同步了呀

    难不成在一次 html page request(Socket connection)中(也就是非 Ajax),你能告诉我怎么能先 write html content 给浏览器(就是 View)再等数据库异步回调再 write 数据的方式吗?

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

    你误解了我的意思。。。我说的不是 Web 响应时间,我说的是网络通讯

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

    为什么 Thin 在 Basic 的时候碾压其他 Server,因为它 I/O 异步了。为什么 View 和 DB 又变差了呢?因为读取模板和查询数据库是同步的。

    你给出来的数据这很明显,Web 框架帮你找到路径之后调用你的业务逻辑肯定会有 View 渲染和 DB 调用,你看看 View 和 View + DB 的差异并不大,说明 DB IO 效率高了去了。

    服务器做的事情,是把 数据 read,然后交给单个 task 线程去处理对应的路径反射你的 Controller 渲染你的 View(太长不写)。你看没有 View 效率多高,最差的情况拖了 6x。让一个性能很好的 Thin 拖得只剩下跟其它一样的水平。

    异步不异步是另外的一个问题,服务器本身 schedule socket queue 是可以异步的。

    但是你一个 Controller 作的事情比如你写了:

    1. 处理 DB
    2. 计算数据,暂且把数据搞了个斐波那契
    3. 处理 View

    难不成你把 Controller 这三件事情分别异步成三个线程,甚至然后做完了再通知去让服务器 response 吗?像移动客户端有些 lib 设置了超时,等你异步做完这些事情,再 write 我客户端估计都已经 close 了。

    先不讨论异步,Webrick 的 parse 是 Ruby 写的,比如 WEBrick::HTTPUtils::FormData

    拿 Ruby 讨论 IO 就是耍流氓

    如果你说用 Go / C++ 在流处理的时候碰到瓶颈,还是值得讨论的。


    2018.3.29 更新:

    但是只关注 I/O 会限制 Ruby 在 Web 服务器以外别的领域的发展

    这一点是认可的,这是在开发通用编程语言,不是开发针对性的工具。

    Ruby 就算没有 JIT,跑得比 C++ / Go 慢,也是值得用的,因为至少这样的工具好用而且编码也舒服,良好的 Ruby idiom 和 style 也是很好维护,无需编译,写起来也先行轻松。

  • 首先,服务器都是开辟/派遣一个 thread 你的一次 action 的,要不就是 process,抢占任务都一个样,等待数据库那是远程调用的事,而且 MySQL 的组件是 C Extension。

    如果不是 Ruby parse 数据包(无论 HTTP 还是 ws 还是别的)太慢你会换成 C 来 parse,Ruby parse 数据包带来的延迟的速度比跟客户端 IO 影响得还要高。

    另外,服务器的 Ruby IO 的问题个人认为是 Ruby String 类 效率问题,你从流读取会把 buffer 读入里面,但是换做 C char 作 buffer cpy 处理,效率完全不一样。

    读了一下 Thread::Green 的代码,其实 FD 通信,本身就是 blocking 的。。。比如 select 就是个阻塞调用,阻塞的地方在这个调用本身。

    GCD 本质确实是 blocking 的没毛病

    RPC 不要拿出来说,分布式就有一个 RPC 一粒粒组件之间调用带来的延时问题。

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

    刚好相反,rails 太臃肿了,IO 开销实际上是很小的一部分,服务器真正做的部分只有通过调度、读 request、写 respose。

    而你的 HTTP parsing、业务处理( rails 本身有大量的对象映射、抽象、创建和销毁等等)才是最大的问题。这些属于计算密集问题。

    mjit 在类型明确一些的 block 是更容易被 jit 编译的,但像 AR(Active Record) 则不然,一层层的抽象几乎不尽相同的数据,动态的元编程动态嵌入方法和属性,阻碍了 old gen code 的形成。

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

    话说这个实现也糙了一点。。。把 hot block 找出来然后 C 化然后 compile 而且还放在 /tmp。重点是,这样一来就依赖 gcc / clang 了。

    我期待的好像不是这样的,有点失望。我期待它能本身提供编译的功能,就是 ruby file.rb 就已经能从 YARV -> 机器码,而不依赖外部工具。不过,实现到那样的 level,估计 Rubinius 会便利一些,后端天然的有 LLVM 垫背。

    如果 MJIT 实现了即时 YARV -> Native Code 即时存储到内存空间预备即时调用,我觉得它干脆做成下一个 LLVM 了。

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

  • 我想表达的意思是。

    关注点有问题,我刚才说的 绕开 GIL 实现并行,实际上是我用 Autumn 的 select 换成了 epoll 加入到 amber-kit 的实验途中,在对象创建关联上是有问题,经过多番调整,后来是通过了,但是,我发现 epoll 写的 iom 只是 IO 本身高效,但是,实际上你写一个 HTTP SV,或者 FTP SV,你都要 parse 协议的,Ruby parsing 效率很低,好,接着咱们换成 C 来实现了 parsing,这就是我上年回复姜叔叔说我把玩具的 协议 parsing 改成了 C extension 去 parse,当前我未释出的 amber-kit 版本达到了 10k+ req / s。

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

    虽然提高了效率,但是实际上问题还在后头,你有业务逻辑代码。

    你写这些东西,终究是为了业务服务的,就像你写网站,终究是为了开网站,开网站有内容,有内容的话数据量会越来越大。

    经过后面 bench,Ruby obj_a -- call -> obj_b -- call -> obj_c 还有 obj_a new obj_b new obj_c 所消耗的时间,远远比我上面说的写的东西耗时要多了去了。

    更何况 Web develop 不像做 OJ 竞赛,没那么垂直和单纯与纯粹,你可以把算法优化到极限不管团队其他人能不能看懂。

    GCD 是对于线程的封装没错,而对于 Thread::Green 这个新事物的观点我的评价是【听起来就像是】,这是我感兴趣、疑问的意思,我不太清楚 Ruby 这边发明这玩意来干嘛。

    MJIT 解决的问题和 AutoFiber(Thread::Green) 解决的不是一个问题 那跟我说的也不是一回事。

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

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

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