精华 fibjs比 nodejs 快五倍
发布于 6个月前 作者 ngot 10061 次浏览

原文[http://baoz.me/449778]

少废话,上代码。nodejs 代码如下:

var http = require("http");
var server = http.createServer(function(request, response) {
    response.writeHead(200, {
        "Content-Type": "text/html"
    });
    response.write("Hello World!");
    response.end();
});

server.listen(8000);

测试结果:

lion$ wrk -t12 -c400 -d30s http://127.0.0.1:8000/
Running 30s test @ http://127.0.0.1:8000/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    38.61ms    6.18ms  77.46ms    84.50 %
    Req/Sec    837.87      134.82      1.77k     81.78 %
  297965 requests in 30.00s, 44.05MB read
  Socket errors: connect 0, read 453, write 0, timeout 90
Requests/sec:    9931.42 
Transfer/sec:      1.47MB

fibjs 相似代码如下:

var http = require("http");
var svr = new http.Server(8080, function(req) {
    var rep = req.response;
    rep.addHeader({
        "Content-Type": "text/html"
    });
    rep.body.write(new Buffer("Hello World!"));
});

svr.run();

测试结果如下:

lion$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/
\Running 30s test @ http://127.0.0.1:8080/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    16.73ms   31.61ms 162.45ms    94.93 %
    Req/Sec     3.25k    822.21      5.28k     89.45 %
  1137450 requests in 30.00s, 160.54MB read
  Socket errors: connect 0, read 282, write 0, timeout 0
Requests/sec:   37916.03 
Transfer/sec:      5.35MB

可是有些代码对 fibjs 纯属多余,精简一下:

var http = require("http");
var svr = new http.Server(8080, function(req) {
    req.response.body.write(new Buffer("Hello World!"));
});

svr.run();

测试结果如下:

lion$ wrk -t12 -c400 -d30s http://127.0.0.1:8080/
Running 30s test @ http://127.0.0.1:8080/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    24.39ms   71.85ms 343.96ms    95.11 %
    Req/Sec     3.99k     1.00k    6.67k     91.61 %
  1379337 requests in 30.00s, 161.80MB read
  Socket errors: connect 0, read 270, write 0, timeout 0
Requests/sec:   45980.00 
Transfer/sec:      5.39MB
149 回复

弱弱的挑衅,不服来战~

再不fibjs,你就软了

前排卖瓜子汽水爆米花

楼主究竟何方妖孽,胆敢来此挑衅。

高度优化的东东,可能不好用?

@ngot 好凶悍,不怕某兔粗来咬死你么:(

@ngot 这是来找骂 的?装逼有意思吗。。

挑衅成功,我去了解一下

这样的测试,意义不大,node.js 平台,默认是单线程的,是内核最小化的, 优化起来也不难。 而且,node.js 默认设置,最多只开到一小半火力,还不到!!

@jeremy16601 不这样,没人来关注呀

@wangxuq 这么没有幽默感。您难道看不出来,我在跟同事开玩笑?

@ngot 好吧,注意言辞哈

写的一个测试用例,你们折腾去吧!

/**
 * Don't use `ab`. `ab` sends HTTP/1.0 in HTTP requests. This causes node to
 * close the connection after each request by sending a `Connection:close` in the response.
 * Adding `-k` for keep-alive to ab does not help.
 *
 * If you use `siege`, make sure to set connection = keep-alive in your .siegerc file.
 * If not, siege defaults to HTTP/1.0.
 *
 * @author liveinjs
 * @version 0.0.1
 */

var http = require('http');
http.globalAgent.maxSockets = Infinity;
var numCPUs = require('os').cpus().length;
var cluster = require('cluster');

if (cluster.isMaster) {
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
    
    } else {
        http.createServer(function(req, res) {
        res.end("hello world!");
    }).listen(8080);
}

@hackerjs node.js 默认设置,最多只开到一小半火力 啥意思?开启多个node进程,就能开足获利了?

@hackerjs 这不合适吧,fibjs已经说了同样是单线程的了

@kingapple 请自行google node性能及安全提升相关的文章,有不少公司分享的。

@ravenwang 看了下介绍, fibjs 是 v8单线程 + 后台多线程,后台线程里做了什么优化是否充分利用多核、内存,我还没看。 不过,node.js 平台 所有的纯js 库,只要你真的需要,你都可以完全通过 编写 C/C++ Addon ,重写个C/C++ 版的。 能把服务器压榨到什么程度,就看你的C/C++水平了,自由发挥! 不过,fibjs 也得支持,关注中。

@hackerjs 之前只看了作者的说法,刚去撸了眼fibjs的readme的确如此,这么说来,且不说重写addon了,比较公平的比较至少应该是两者都想法把多核跑满再比

http://baoz.me/fibjs 试了一下,确实有点快。

又撸了撸FibJS的文档,作者最得意的应该是coroutine相比回调的简洁优雅(且不提generator),于是我去看coroutine,发现这货根本不是coroutine,而是个Fiber管理器,至于Fiber,是个更轻量线程,而且我在coroutine接口里看到了恐怖的东西:LockSemaphore,有了这俩玩意还让不让人愉快地写异步代码了…

我底层懂得少,如果讲了什么小白的话请往死里批

第一次知道fibjs,有点意思,哈哈

这个测试代码可以再幼稚点吗?

测试代码要以操作系统通信为准,包括但不限于文件io,tcp…

有时间看看,顶南京西祠胡同的响马!先赞下

@ravenwang 有了fibjs还写异步代码干神马

@ngot 唔对不起,我表达错误,这里不讨论速度,说的是fibjs用所谓的coroutine解决异步的问题,从代码编写和理解的难度来说,同步<异步<<多线程,fibjs是希望用同步的方式写异步代码来降低理解难度,但方案却是把多线程那套东西给引进来了

如果我理解正确的话,Fiber是并行的,这会产生共享资源占用的问题,所以引入了LockSemaphore来解决这个问题,我不清楚fibjs中类似于两个Fiber共用同一闭包时的代码要怎么写,如果连这个都要用Lock解决的话,我只能呵呵了

居然给转这边了,统一回复一下吧。

这并不是一次严肃的对比测试,只是一时兴起测着玩的,不必认真。简单说结果吧,孢子社区已经在 fibjs 上开发了数万行代码的服务器应用,并且稳定运行一年多。

有同学看见锁就惊恐,是不对的。nodejs 在需要资源受限时同样需要锁,并且异步锁的实现令人作呕。协程的好处是你不需要对内存加锁,但是不代表你不需要对资源加锁。比如限定数据库连接数,阻止缓存重复更新,等等,很多高级场景都是需要锁来保障的。

同时,nodejs 从来就不是单线程,同样是单线程 v8 + 后台多线程,与 fibjs 相同,并且 nodejs 的 http parse 也是 c 写的。至于说 nodejs 也可以通过 addon 来提升性能,那就强词夺理了哈哈。

最后用我在 fibjs 论坛里的一段回复结束吧:

高并发不等于高性能,随着应用层逻辑的引入,会拉平甚至拉低不同平台的并发性能,越复杂的应用,对语言本身的运算速度依赖越大。

因此拿简单的 echo 来对比并发其实是没有意义的,不同语言,开发环境,最后实际上瓶颈根本都不在基础架构上,大部分应用最后每秒能响应 100 个请求便算上很好了,基础架构每秒 5000 还是 50000 对此影响很小。

所以结论是应用开发框架比并发性能是一件很蛋疼的事情,开发范式的改变带来的应用可读性,模块低耦合,容易工程化所带来的性能提升反而收益更高。

何况 fibjs 就连基础的并发性能也秒杀 nodejs 哈哈。

@xicilion 这是来抢用户么

@xicilion 的确是高精尖的东西, 需要支持. 相比node.js的优点在哪: 五倍速度? 同步方式写异步? 如果只是这两点fibjs还没发撼动Node.js生态, 如果速度有几十倍的差距则另当别论

最大的问题是不能使用Node的包,难道又要重复造轮子

@coolicer 只有彻底抛弃异步写法,才能获得重生。

@ngot 个人觉得callback 才是乐趣.哈哈~~

@lonso 你内心真强大,我表示受不了。callback是对我身心的摧残。

@xicilion 我用Node时间不长,还没遇到过js代码需要加锁的情况,能举个例子说明下令人作呕的异步锁吗? 前面有同学提到改用C/C++重写addon怎么能算强词夺理呢,fibjs目前的速度优势主要应该在于把Node中部分用js实现的库用C/C++多线程实现了吧,前面也提了要比速度的话比较公平的方案应该是大家都把多核跑满来比 你也说了基础架构上5倍的速度根本不值一提(是不是真能有5倍的优势另说),我对fibjs感兴趣的也是用同步方式处理异步代码的能力,目前看到的也仅仅是提供了coroutine,相比Node加个coroutine的包相比也没啥优势,实在没看到Node转fibjs的任何理由

@lonso 我觉得2层还能接受,再多就是折磨。

Coroutine只是一种异步处理方案而已,怎么能算抛弃异步了,况且Node又不是只有Callback,ES6又有了Generator

baoz的宣传贴?

@ravenwang 是啊,何况后端不同前端,完全可以用最新的技术 :)

表示fibjs还不是特别的完善吧,Google团队好像还在不断的更新哎。不过确实可以学习学习。。。

@ravenwang 例子我说了,当你需要限制数据库连接的时候,或者缓存失效,需要更新缓存,但是多个请求同时请求,导致多次不必要的更新请求。

你说的都是真理,如果你把 fibjs 做的事情按照 fibjs 的方式在 nodejs 上做一遍,当然没有迁移到 fibjs 的理由。你赢了哈哈。

不过前提是你先写 4万行 c++ 代码。

@goodkids fibjs 和 nodejs 都是基于 v8 的,这一点没有区别。fibjs 每周都会跟踪最新的 v8 升级。

@ravenwang 建议深度试用 Generator 之后再做比较。

@ravenwang 另外,fibjs 不是仅仅提供了 coroutine 这么简单。fibjs 实现了一整套同步 io 模块,网络模块,数据库接口等等,是一套完整的开发框架。

如果只是 v8 + coroutine,两百行就写出来了。你把 node 挂上 coroutine 包,面对一大堆不能用 coroutine 的各种 io, net, db 模块,你能干什么?

@xicilion 我没什么要赢的这也没什么输赢可讲,你能做出fibjs并且开源我非常佩服,既然贵司员工来这里推广(在我看来推广方式并不怎么友好),于是我就站在一个小白用户的角度看一看,提出我自己的疑虑而已,我也没有挺Node的意思,只要fibjs给我足够的理由我会毫不犹豫地迁移过去

目前来说,从我所看到的fibjs的文档以及样例代码,基于我所能理解的知识范围,fibjs所提供的coroutine相比Node所能支持的其它异步方案比如promise、generator、甚至是coroutine等在编程范式上并没有多大的优势,而性能上也没有压倒性的优势,也许一个更完整的demo对比才能体现出fibjs的强大,或者说能让我这样的小白理解fibjs的强大

祝fibjs能获得更多关注和支持

@xicilion 在Node下就要用异步回调的思想写代码,至于像限制链接数、等待更新缓存这种,在Node的世界都应该是采用回调队列这种思想来处理,不需要锁……

@coolicer callBack挺好的……

关于Callbacks , Coroutines和 Generators。看看TJ如何评论。[https://medium.com/code-adventures/callbacks-vs-coroutines-174f1fe66127]

@xicilion

同步在异步面前,等于浪费资源,等待,等待,再等待…

CPU是有限的,金钱也是有限的,这就是现在NGINX大胜APACHE的原因。 连SCALA和GO都在探索异步驱动编程。 而我认为你的这个方式反而是一种倒退。

@ngot

你压根就不懂nodejs,所以不要用你的这些东西来对比了,没有意义。 你甚至连异步是什么都不理解。

你所谓的这个东西,用scala、go写的能甩几十条街。 而且,有一点,如果你想对比测试,就测试文件IO,数据库TCP通信, 测试CPU输出有什么样的参考意义?res.write()这种代码毫无意义。

要测试就测试fs.readFile()fs.writeFile(), 以及数据库TCP.

@tulayang 呃…fibjs比Node异步得更狠…… 同步的只是写代码的方式而已

@kingapple 卧槽,某兔不会是指左边这只兔吧……

@ngot TJ文中所讲的Coroutine是指Full Coroutine,目前FibJS在JS层面所提供的Fiber是没有那个能力的

说道这我好像突然明白FibJS的理念了,是把底层Coroutine化,JS接口部分改成形式上同步实质上却是异步执行的,这个确实牛逼,我之前只是在看JS部分所提供的东西,所以一直没理解,也就是说,在FibJS中:

var f = fs.open('test.txt');
f.write('something...');
f.close();

类似于上面这样的代码,每一步都是异步执行的,这样的确可以称得上是在范式层面的巨大进步

@ravenwang

这种说法就是自我主观意识,就好比我认为我开发了个比linux还多用户多任务的操作系统。

@tulayang 看我最下面的回复吧,fibjs确实牛逼

@tulayang fibjs 和 go 的模式是一样的,后台全部异步,前台面对开发者,将异步映射为同步协程。

协程方式上,fibjs 也是采用与 go 同样的模式,不是使用 gen 伪造协程,而是通过栈切换完整保存现场。这样的好处是在 javascript 层完全不必考虑实现方式,只需要按照最直接的思维方式写出业务逻辑即可。

[@ravenwang](/user/ravenwang) 说 fibjs 比 node 异步更狠还是蛮准确的,fibjs 的工作线程是全部由 c++ 完成的异步逻辑。换句话说 Node 是在用户线程异步,fibjs 则是把异步延伸到后台大量工作线程。

不过 [@ravenwang](/user/ravenwang) 说同步只是写代码方式这一点不准确,如前所述,fibjs 不是语法上的技巧,而是硬碰硬地切换堆栈。所以不是写代码方式不同,而是 javascript 代码实实在在就是同步的。

@ravenwang

"形式上同步实质上却是异步执行"根本就是可笑的。

世界上不存在“形式上同步实质上却是异步执行”。

如果你要用语法糖来伪装,那也不过是在内部封装了一个结构来缓存要处理的程序。

@xicilion

nodejs的异步逻辑也是c++完成的,js本来就是解释性的语言。

v8是c++的,js的事件驱动过程本来就是c++完成的。

nodejs的整个事件驱动完全是交给v8,v8有多快,他就有多快。

js的性能瓶颈仅限于js代码的循环,和数字、字符向c++的转换。

@xicilion 如果写段类似上面的代码作为样例而不是那个什么当当当当,理解起来要容易很多……

@tulayang "形式上同步实质上却是异步执行",你每天使用的操作系统每时每刻都是这样运行的。

@xicilion

这是不一样的。

多进程非异步并发,跟多进程异步并发,有本质的区别。

比如2个CPU,同一时间都可以参与2个事务。 但是异步,在接下来的时间则可以继续参与其他事物,非异步则只能等待。

他们都是多进程并发。

@tulayang 好吧我做个类比。fibjs 的工作原理跟 goroutine 是完全一样的。

@xicilion 你们继续战,我喝啤酒去咯,哈哈哈

@ravenwang 我也遁了,跟一个 bug 较劲呢。

@tulayang bug 没搞掂,回来说两句。

首先,fibjs 不是语法糖,跟你见过的其他各种 js 伪同步的东西不一样。另外,并发不论是多进程还是多线程,都是在操作系统协调下的后台异步前台同步,你的文件读写,控制台输入输出,甚至同步网络操作,都不会出现你说的等待的时候 cpu 等在那里的情况。因为操作系统在发现同步线程需要等待的时候,便保存现场,把 cpu 分配给别的线程了,等底层异步完成后,再触发事件唤醒等待的线程或进程。

fibjs 采用的是用户空间线程技术,windows 上也叫纤程,这个其实是很古老的技术,远在 Linux pthread 出现以前,就有一个叫 pth 的项目 帮助 Linux 程序员实现纤程。这种用户空间线程不同于操作系统线程,是非抢先的,必须依赖于各个纤程主动交出控制权,其他纤程才可以被唤醒。

pthead 出现之后,这种技术慢慢被人淡忘,直到大家越来越发现在应用层处理多线程带来的运行成本和程序员不熟练带来的风险。

异步处理便是一种尝试,不过也并不是什么新技术,哪怕最古老的操作系统,内部实现也都是异步的。unix select 开始把这种范式带到应用层,windows gui 的消息处理也是异步编程,其它的比如 Windows 的完成函数,iocp,linux 后来赶上的 epoll,freebsd 的 equeue,都是运用了十几甚至几十年的技术了。

然而毕竟异步是非人类的。于是越来越多的架构开始努力不那么异步,nodejs 社区便做了很多努力。而目前为止,最让人称道的恐怕算是 go 的 goroutine 了,goroutine 便是典型的用纤程来实现用户层同步,基础库异步的方案。而 javascript 以纤程为基础提供完整解决方案的 唯 fibjs 一家,nodejs 虽说也有第三方 coroutine 模块,但是只实现了最基础的纤程处理,而整个 nodejs 生态都是建立在异步基础上的,导致 coroutine 根本不能发挥,因此几乎一发布便被遗弃了。

用户空间线程因为是非抢先的,意味着不会出现多个纤程同时修改和访问内存,不会出现多线程开发的绝大多数风险,但是缺点是因为所有纤程其实共享一个物理线程,所以也会出现 nodejs 一样的运算瓶颈。

针对这个情况,fibjs 的策略是只在极少数必须的运算采用纤程,而所有可能需要大量运算,或者需要等待的 io,都投射到后台工作线程,或者异步完成,尽可能把宝贵的 javascript 线程的运行时间留给业务逻辑,从而最大化 cpu 利用率。

这样做的代价便是 fibjs 的基础库很难像 nodejs 那样大量使用 javascript 实现,只能用 c++ 完成几乎全部基础库。

@ravenwang 我有疑问。

就你举的这个例子,是否可以理解为:这三句JavaScript代码是阻塞执行的,而它们调用的底层C++模块是异步执行的?

如果是这样,那显然跟Node.js有差别。试比较:

var fs = require('fs');
fs.open('/some/file', 'w', function(err, fd) {
  // write data to the file
  fs.write(fd, buffer, 0, 100, function(err, written, buffer) {
    fs.close(fd, function() {
    });
  });
});

var total = 0;
// more processing

上面的代码,调用的文件处理模块当然是异步的。而JS主线程是非阻塞的,也就是会直接继续执行var total=0这一句。如果换成fibjs,是否意味着主线程要等待文件操作执行完,才执行这一句?

@leizongmin (⊙o⊙)… 赶脚亲好神奇啊:)

@xicilion

js伪同步? 我是认为你的fibjs c++层面使用了语法糖,用结构来缓存处理程序而已。

操作系统的程序当然不需要等待,尤其是linux unix这种。但是服务器程序,却会等待。

服务器程序要跟操作系统交互,就要等待操作系统的返回值。如果你不是异步,那你的服务器程序必然要等待,这就要阻塞。你所说的不等待,只不过是cpu的记忆、切换而已,但是处理程序却是在等待的。

如果程序只能控制两个cpu,那么非异步,只要任何一个没有完成任务,不论你cpu再怎么切换,业务程序都是处于等待状态。他要消耗两个内存单位,占用两个cpu处理单位,任何的后续业务都会被阻塞。

至于线程,nodejs是不使用的。

我觉得,nodejs作者跟nginx作者一样,在设计的时候,只选择了类Unix系统,多进程操作系统。windows在多进程上的初始化开销和速度,使他并不适合这种方式。至于后来有了windows版本,那也不过是微软的努力而已。

而类Unix系统,其目标就是多进程,而非多线程,这使得他所建立的任务安全可靠、易调试,没有副作用,他所建立多进程的开销比windows小的多得多。

我不认为nodejs使用多线程有什么好处,这会引起大量的副作用,他的职责应该是消息通信,而不是复杂计算。做顶点计算,随机序列,不是他的目标。当然,有人希望nodejs做得更多,我却认为这是画蛇添足,把nodejs往java的老路上推。

如果需要nodejs做得更多,我觉得更好的方式是: nodejs <----------------------> 操作系统 <------------------------> c c++ java …业务

既没有同步等待,也没有副作用,而且由c c++ java …负责计算,nodejs只是负责告诉操作系统,我需要什么。 现有的例子莫过于c++写的数据库业务了,而这个数据库根本不需要用nodejs去写。 而这个业务也同样可以是顶点计算,序列…

nodejs就好比是列车,而复杂计算则是货物,从没有人期待nodejs即做列车又自备货物。

@bnuhero 同样效果fibjs要这么处理

var fs = require('fs')
coroutine.start(function() {
  fd = fs.open('/some/file', 'w')
  fs.write(fd, buffer, 0, 100)
  fs.close(fd)
}
var total = 0

调用fs.open()时看起来当前正在执行的代码是阻塞了,但v8也就是JS线程是不阻塞的,fs.open()之后,控制权就转给了底层协程引擎,协程引擎会调度其它Fiber来执行,比如处理多个请求时每个请求对应一个Fiber,这时就可以处理下一个请求了

这里的关键在于上面的代码完全是传统的同步写法,却同样享受到了Node异步的好处

@An-leb

说就说句有点含量的话,不懂就别说,说个P话让我觉得你压根就不懂编程是什么东西。

@ravenwang

所以,在fibjs中,执行JS代码的 --> 是支持用户态多纤程的单线程V8引擎实例,是这么理解的吗?

因此,所有的核心模块(包括fs在内)都得用C++重写以支持纤程的工作方式。

另外,是否与JXcore做过对比,谁的处理速度更快些?JXcore有一个好处是,100%与Node.js兼容。

@tulayang 建议你了解一下coroutine/fiber和线程的区别,fibjs里的等待底层返回和node里等待底层调用回调函数是一样的,这时v8是不阻塞的,可以继续处理其它fiber,简单来说fibjs只是把回调函数换了一种写法而已

@bnuhero 是的,而且我觉得因为基本的设计哲学不同,fibjs应该几乎没法利用node的现有成果 fibjs的优点不是速度(当然这是相对于node来说),它跟node是站在同一个基础之上的,从理论上来说即使快也快不了多少,fibjs解决的是node/javascript一直在试图解决的异步编程模式问题

@ravenwang

这个没什么重要的,操作系统要处理任务,除了进程单线程就是进程多线程,你任何的手法不过是建立在这两者基础之上。

@ravenwang

你这些所谓的“同步代码”,不过是把语法糖从js移到了c++,光这一层面没有实质意义。

用栈或者其他压入还未初始化完成的逻辑,等待完成再出栈运行。

把js代码移到c++,这没有任何意义,而且是画蛇添足。

单从OO上,js足够处理这种组合模式。

从FP上,js简直就是小儿科。

不要忘了,js拥有c c++所不拥有的lisp FP特性,js被发明出来本就是简化这种任务,而现在你却反过来在c++里写。

4万行代码,我看充其量也就js 4k行的功能。

@tulayang 原理不重要。学习了。

@kingapple 你这是赞我还是贬我呢

这是国产的吗?好牛X的样子。

@bnuhero fibjs 和 JXCore 解决的问题是不同的。JXCore 试图解决 nodejs 单线程瓶颈,他的实现方式比较简单,是在进程中开启多个 v8 内核,各自在自己的空间和线程内工作,好处是可以充分利用多核(类似 cluster 的单进程版),缺点是每个 v8 内核是独立的,内核之间传递变量,函数,共享,都必须经过处理,简单来说就是进程内的 rpc。

@ravenwang

Yes, there have been discussions. I think, I won’t be adding coroutines. I don’t like the added machinery and I feel that if I add them, then people will actually use them, and then they’ll benchmark their programs and think that node is slow and/or hogging memory. I’d rather force people into designing their programs efficiently with callbacks. ----- Ryan dahl

@ravenwang fibjs 因为 异步-> 同步转换的原因,其实很多地方性能是低于 nodejs 的,但是实测起来,性能却高于 nodejs,根本原因,还是编程范式的改善,极大简化了业务逻辑,带来的性能提升。

同时 fibjs 还会转化工作方式,在只发现一个纤程时,不需要使用后台线程,则自动选择纯同步模式,以避免不必要的任务切换负载。

比如同样的 leveldb 封装,多纤程下,fibjs 性能是 nodejs 的三倍,单纤程下,则是 nodejs 的接近 9 倍。(这个话题拜托别展开,我只是发现封装后性能比 leveldb 官方声称低一个数量级,才找来 nodejs 的封装测一下的,同样不是认真的性能对比测试,不必认真)

这一点在业务逻辑层尤为重要,简化的编程范式带来的性能提升,反而远大于核心库优化带来的性能提升。毕竟绝大多数应用的瓶颈,其实都是复杂的业务逻辑,而非核心库本身。

@ravenwang 这段代码让我想起了jscex

看了楼上各种大大的言论,吓得我都不敢说话了。

首先撸主的确很流弊哈,只不过目前最大的软肋就是生态圈抵不过——除非撸主去把 npm 的包给搞到自己的镜像然后宣称兼容。

以及——其实在 0.11 的 node 版本一下,不知道大家有没有用过 fiber 这个 C++ 的 addon。

@XadillaX 这个问题我在上面说过:

javascript 以纤程为基础提供完整解决方案的 唯 fibjs 一家,nodejs 虽说也有第三方 coroutine 模块,但是只实现了最基础的纤程处理,而整个 nodejs 生态都是建立在异步基础上的,导致 coroutine 根本不能发挥,因此几乎一发布便被遗弃了。

同时,fibjs 生态的确比不过 nodejs,这一点必须承认,哑口无言哈哈。

@leizongmin 兔纸大神令坏人闻风丧胆,必须赞的:)

@tulayang 你是不是不知道 fiber 是什么?

@xicilion 马爷发现了真相。

@tulayang 感觉你越走越远了… 不但nodejs本身是js线程+后台多线程的,你在nodejs中的js代码,只要你乐意,也可以创建进程(child_process)和线程(webworker-thread),至于是基于事件还是基于同步原语,你完全不必拘泥于单一的编程范式。

纤程之于线程,类似于线程之于进程,你把fibjs里面的js线程想象为一个进程,把纤程想象为用户态的、非抢占式线程,也许很多东西你也就可以想通了。你拥有了在单一的线程时间片中自我调度执行序列的能力,并可以安排给不同的任务去伪并行执行,这与**“用栈或者其他压入还未初始化完成的逻辑,等待完成再出栈运行”**完全不搭界的啊。

对于习惯异步方式还是同步方式来编码,完全是个人选择问题,但如果走进了用callback搞定一切的单一思维方式,那也就完全限定了自己的能力。

同样的,“不要忘了,js拥有c c++所不拥有的lisp FP特性,js被发明出来本就是简化这种任务”,这话也没一点道理,且不论js的历史,大家都知道;C++对函数式编程范式并不是完全没有支持,C函数指针不说了,模板元编程是有的,lambda也是有的,(嗯,还有操作符重载,甚至#define Macro),高阶函数和偏函数都提供了优雅的支持。

代码都是人写的,多了一种工具也就多了一个可以玩的东西,也多了很多的可能性,你还是你,又没有改变。Coding的世界这么大,没必要在了解之前就去排斥吧。

@zalemwoo

nodejs是建立在事件列表,主进程-操作系统-子进程,在直线上只有一个进程一个线程。

跟你说的什么“js线程+后台多线程”没有关系。

@xicilion 远还没到**"完整解决方案"**的程度吧。。。

同时,fibjs 生态的确比不过 nodejs,这一点必须承认,哑口无言哈哈。

生态圈慢慢来,公开才没几个月,很厉害了

@xicilion

  • 请求,创建一个新的进程,处理请求
  • 请求,创建一个新的线程,处理请求
  • 请求,放入事件列表,主进程非阻塞I/O

怎样算做知道呢,再多的手法,到了c c++也无非是压栈,出栈,一个主队列。

@tulayang 我书读的少,你不要骗我

@tulayang 你确定?

$ ps
  PID TTY          TIME CMD
50804 pts/3    00:00:01 bash
58260 pts/3    00:00:00 node
59199 pts/3    00:00:00 ps
$ ps -L 58260
  PID   LWP TTY      STAT   TIME COMMAND
58260 58260 pts/3    Sl     0:00 node --harmony c.js
58260 58261 pts/3    Sl     0:00 node --harmony c.js
58260 58262 pts/3    Sl     0:00 node --harmony c.js
58260 58263 pts/3    Sl     0:00 node --harmony c.js
58260 58264 pts/3    Sl     0:00 node --harmony c.js
58260 58265 pts/3    Sl     0:00 node --harmony c.js
$ ls /proc/58260/task/
58260  58261  58262  58263  58264  58265
$ find /proc/58260/task/ -name stat -exec cat {} \;
58260 (node) S 50804 58260 50804 34819 59202 4202496 5157 0 41 0 38 18 0 0 20 0 6 0 51725428 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140734762386728 140245676173961 0 0 4096 134234626 0 0 0 17 0 0 0 16 0 0
58261 (OptimizingCompi) S 50804 58260 50804 34819 59202 4202560 93 0 2 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245675281904 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
58262 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695659600 140245679055904 0 0 4096 134234626 0 0 0 -1 1 0 0 0 0 0
58263 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695532624 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
58264 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695462992 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
58265 (v8:SweeperThrea) S 50804 58260 50804 34819 59202 4202560 0 0 0 0 0 0 0 0 20 0 6 0 51725430 685854720 2700 18446744073709551615 4194304 14908980 140734762399824 140245695393360 140245679055904 0 0 4096 134234626 0 0 0 -1 0 0 0 0 0 0
$ 

@zalemwoo 哈哈,完整当然是相对的,这里指的是开箱即用,是有完整的多任务调度和并发控制,完整的并发文件 io 和 网络 io,一套完备的 http 客户端服务器端解决方案,ssl 支持,以及数据库支持。

对于绝大多数场景,fibjs 的基础库已经足够应付了。

@zalemwoo

这个你无需向我取证。

多线程就是多线程,事件驱动就是事件驱动。

@tulayang 看来的确不了解,建议学习一下 goroutine,熟悉 c 的话学习一下 linux 的 getcontext, makecontext 和 swapcontext,熟悉 windows 的话了解一下 Fibers。

fibjs 为了保证可移植性,连这些都没用,是自己实现的 fiber。但是原理都是一样的。

再说一句,windows 的 fiber 的历史有快 20 年了,Linux 的 context 历史可能也有十年了。我前面提到的 pth 是在 linux 支持线程之前便有的方案,但是实在太久远,不建议了解了。

@zalemwoo

另外,“c++对函数式编程范式并不是完全没有支持”,这不是很可笑的吗?

一个语言,就算你添加了lambda也不代表你运作的了函数式。 c++已经是一个很可笑的语言了,添加了函数式的c++就是更可笑了。

如果这世界只剩下c和c++,我绝对不会选择c++。

@xicilion

我对windows没有任何兴趣,对于windows思想搭建起来的服务器更没有兴趣。

对于服务器来讲,我觉得线程是多余的,对于图形编程,线程也许是必须的。

副作用、记忆带来的内存CPU损耗,这都是服务器所不喜欢的。

任何以hello world做性能对比的测试,都是耍流氓。

@JacksonTian jscex后来怎么了?

@JacksonTian 我也是想到了jscex,理念类似,但却是完全不同的东西

@ravenwang 这个我知道,我想知道后边的故事,怎么后来就不维护了?

@xicilion 于是我有一个问题了。。纤程是不会被主动执行的,你在调用执行大批量纤程的时候使用的方式是什么?我是真的不熟悉fibers。。求指教

@SeanLiangYoung

function func() {
}

func.start();

便会创建一个纤程,将 func 作为入口,并加入调度列表,当有纤程出让控制权时,此函数便会被执行。切换方式是保存全部 cpu 寄存器,加载目标纤程的 cpu 寄存器,恢复 ip 和 sp。这是标准的纤程处理方式。

能不能编译几个 二进制包, 拿 VS14 编译失败, VS2010 编译也失败 囧

@slightboy Windows 在 vs 2013 上编译测试的。之前发布过二进制,不过因为项目一直在更新,二进制发布总是滞后,后来便没做了。

@xicilion 可以建个 daily build, 人工编译发布 是麻烦的

还没有社区啊,感觉也不能应用于生产,即使真的某方面超越了,也还是观望观望。。

@airyland 关注fibjs,关注孢子社区 http://baoz.me

现在能加快人的速度而不是机器的速度的技术才是好技术。其他都是浮云. Fibjs看似挺好,先把在线文档做好再说. 先支持一下.

jacksontian node $ ./tools/wrk/wrk http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     4.19ms    7.15ms  54.22ms   93.58%
    Req/Sec     3.91k     3.82k   16.33k    81.44%
  70239 requests in 10.01s, 9.91MB read
Requests/sec:   7016.96
Transfer/sec:      0.99MB
jacksontian node $ ./tools/wrk/wrk http://127.0.0.1:8000/
Running 10s test @ http://127.0.0.1:8000/
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   840.83us  358.15us   5.87ms   85.23%
    Req/Sec     6.41k     1.21k    9.70k    64.59%
  121425 requests in 10.00s, 17.95MB read
Requests/sec:  12143.27
Transfer/sec:      1.80MB
jacksontian node $ ./tools/wrk/wrk -t12 -c400 http://127.0.0.1:8080/
Running 10s test @ http://127.0.0.1:8080/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    18.40ms   24.78ms 131.81ms   95.24%
    Req/Sec     1.55k     0.95k    5.96k    63.82%
  169208 requests in 10.01s, 23.88MB read
  Socket errors: connect 155, read 235, write 0, timeout 653
Requests/sec:  16907.91
Transfer/sec:      2.39MB
jacksontian node $ ./tools/wrk/wrk -t12 -c400 http://127.0.0.1:8000/
Running 10s test @ http://127.0.0.1:8000/
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    24.90ms    2.52ms  46.70ms   64.25%
    Req/Sec   817.54    397.28     1.77k    70.75%
  96824 requests in 10.00s, 14.31MB read
  Socket errors: connect 155, read 61, write 0, timeout 620
Requests/sec:   9679.52
Transfer/sec:      1.43MB

以上 8000是node,8080是fibjs。看起来跟连接数有些关系。

连接数少的时候,node性能还略好些呢。

连接数多的时候,差距没有5倍那么大。

以上测试仅供参考。

@tulayang 赞同!NodeJS是胶水,不要把什么东西都搬进来,那样最后会很痛苦的!

fiber和callback之争,在node社区争论了老九了,理论上callback性能比需要保存现场的fiber要高,但是callback写起来比较恶心,尤其是在err处理上

不明觉厉,围观中…

@xicilion 试用了来着,写的很爽啊

不说别的,用例为什么nodejs版本和fibjs版本一个用Buffer一个用String……

@fantasyni 用async来写callback及err处理,感觉还是挺方便的~

@scaret fibjs内部做了自动转换处理,方便程序员使用。

说句公道话, 前端js写过好多, nodejs也玩过, ES6生成器也折腾过. 异步回调看着优美, 实际用于生产环境真的是开发代价太高. 容易出问题, 调试麻烦. 生成器特性一度让我看到希望, 但是真正折腾进去后, 完全就是一个地狱来到另外一个地狱, 写法特别恶心人. 后面发现了fibjs, 同步的思想来开发, 的确是可以提高很多的开发效率和减少错误的发生. 一些异步产生的错误是偶发性的, 先不说重现的难度, 就算你知道问题, 你要去解决这个异步执行的流程也是非常曲折.

fibjs的确是支持不足, 基本api功能还有欠缺(其实是我的使用比较特殊, 业务性的api接口比node要丰富, 起码是所有数据库模块都已经有了)

经过研究的结果就是, 把已经写好的node底层框架全部从写成fibjs实现. 现在是node+fibjs, websocket还没有第三方库支持, 暂时只能用node的socket.io

@iwinmin 楼上还提交了一个 mongodb 补丁。

@xicilion 有没有好的fibjs调试方法推荐一下.

@xicilion 比較一下幾種寫法:

nodejs with callback

var fs = require('fs');
fs.readFile('in.txt', 'utf-8', function (err, txt) {
    if (err) { throw err; }
    console.log(txt);
});

fibjs

var fs = require('fs');
coroutine.start(function() {
    var txt = fs.readFile('in.txt', 'utf-8');
    console.log(txt);
});

nodejs with co & generator

var fs = require('fs');
var asyn_readFile = thunkify(fs.readFile);
co(function * () {
    var txt = yield async_readFile('in.txt', 'utf-8');
    console.log(txt);
}); // return a promise

nodejs with es7 (in future)

var fs = require('fs');
var async_readFile = asyn function(file, opts) { .... };
var txt = await async_readFile('in.txt', 'utf-8');
console.log(txt);

其中第三種就是我目前常用的寫法…… 通過 thunkify 這個工具(用處:fn(arg1,arg2,…, cb),等價于 thunkify(fn(arg1,arg2,…))(cb) 僅就避免回調地獄這一件事來説,已經達到和 fibjs 和未來的 es7 同樣的結構了; 而批量的 thunkify 以及 forEach 也很容易用簡單的工具方法實現。 它的優點是不言而喻的——兼容一切現有標準 callback 函數。

在這種情況下,換用 fibjs 的意義還有多少?

p.s. 真心請教,沒有隱含“沒意義”這種答案的意思……

@withinsea 首先你的fibjs例子是错的;其次,node不管怎么写,都太麻烦,不直观,成规模的使用就是地狱;再次,性能,还是性能!node性能不行啊。

@withinsea fibjs 是这样的

var fs = require('fs');
console.log(fs.readFile('in.txt', 'utf-8'));

@xicilion 又看了遍帖子 ,突然想到,fibjs可否提供兼容nodejs的全套API,这样就可以利用node的生态了。 自己的同步API可以比如加fib前缀来调用。 这样可以即利用node的生态,也可以使用fib的同步开发模式来开发。

@wuliao49 翻来覆去考虑很久,最后决定不去兼容 nodejs 的 api。

一、完全兼容工作量太大,还要支持丑陋的回调,太让人反胃。 二、还有很多事情要做,从我的项目而言,计划中的模块比兼容 nodejs 价值要大得多。 三、node 巨大部分模块稍作修改便可使用,没有大家想象中的那么不兼容。

@xicilion 如果兼容的话,相信大量的开发者会涌入,推广,普及都会比现状好的多。 更何况现在NODE本身就分裂了,祭起 同步&性能&兼容 的大旗,一呼百应,好时机啊。

题主需要冷静一下,回答全部问题都抱着“回调恶心反胃这样的先入为主基调”,这样讨论就没意义了。 解决 “回调地狱” 是一个问题,但不能因此就说回调丑陋,我反而觉得回调是一种非常棒的体验。 都说异步反人类,其实只是反了你们惯有的编程逻辑罢了,生活中的事物大部分不都是异步的吗? 所以说异步才是真正迎合人类,迎合更多的现实场景。 以往的全同步思维才是反人类,是机器思维。 言毕,仅此而已,有点跑题,并不是讨论fibjs和node

@captainblue2013 对于有争议问题的讨论,其实就是表达自己的观点,让对方接受。在讨论过程中,肯定要始终坚持贯彻自己的观点。如果自己都不坚持,怎么让别人接受呢。我所有的讨论其实就是为了,让大家接受回调丑陋这个观点。当然,你可以反对。

@ngot 坚持己见不等于无法被说服,听别人的意见,首先要从公平理性的角度去理解,然后如果还是觉得自己对的, 娜当然还是坚持自己的意见。

作为一个老年新人完全不知道你们在说什么。

@captainblue2013 同步或者异步,只是事务的运行方式,并不是事务的逻辑,所有事务的逻辑都是以同步方式描述的。异步编程,实际上是把同步描述好的业务逻辑投射到事件处理函数内。不用讨论,这确定是反人类思维方式的。

非常同意,异步不可能反人类,而是顺应人类思维出现的必然产物。 一个非常简单的例子就能说明人们都在异步:

比如,你要切菜,烧水,煮面,煮菜。 如果你是同步执行,那就是先切菜、再烧水、最后煮面煮菜。 如果是异步执行就是先烧水、烧水时切菜、水开了煮面、煮差不多了就加菜。 人类的思维方式一览无余。 存在既有合理之处,问题总是能解决的。

回到顶部