为什么node.js错误地处理setTimeout(func,1.0)?

Hon*_*ule 9 v8 node.js

在处理对时间敏感的项目时,我使用下面的代码测试可用的时序事件的粒度,首先在我的桌面计算机上测试Firefox,然后在我的Linux服务器上测试node.js代码.Firefox运行产生了可预测的结果,在1ms超时时平均为200 fps,并指示我有5ms粒度的时序事件.

现在我知道,如果我使用超时值0,则构建的Chrome V8引擎Node.js实际上不会将超时委托给事件,而是立即处理它.正如预期的那样,这些数字平均为60,000 fps,显然在CPU容量处理(并通过top验证).但是,在1ms超时的情况下,数字仍然在每秒3.5-4,000个周期()之间,这意味着Node.js不可能尊重1ms超时,这将产生理论上每秒最多1000次循环().

玩一系列数字,我得到:

  • 2ms:~100 fps(真正的超时,表示Linux上10ms的定时事件粒度)
  • 1.5:相同
  • 1.0001:相同
  • 1.0:3,500 - 4,500 fps
  • 0.99:2,800 - 3,600 fps
  • 0.5:1,100 - 2,800 fps
  • 0.0001:1,800 - 3,300 fps
  • 0.0:~60,000 fps

setTimeout(func,0)的行为似乎是可以原谅的,因为ECMAScript规范可能没有承诺setTimout将调用委托给实际的OS级中断.但任何0 <x <= 1.0的结果显然都是荒谬的.我给出了明确的延迟时间,n次呼叫的理论最小时间应为(n-1)*x.V8/Node.js到底在做什么?

var timer, counter = 0, time = new Date().getTime();

function cycle() {
    counter++;
    var curT = new Date().getTime();
    if(curT - time > 1000) {
        console.log(counter+" fps");
        time += 1000;
        counter = 0;
    }
    timer = setTimeout(cycle, 1);
}

function stop() {
    clearTimeout(timer);
}

setTimeout(stop, 10000);
cycle();
Run Code Online (Sandbox Code Playgroud)

mae*_*ics 5

来自node.js api docssetTimeout(cb, ms)(强调我的):

重要的是要注意,你的回调可能不会在几毫秒内被调用 - Node.js 不能保证回调何时触发的确切时间,也不会触发排序事件.回调将被调用为尽可能接近指定的时间.

我认为"尽可能接近"意味着与实施团队不同而不是你.

[编辑]顺便说一下,似乎任何规范都没有规定该setTimeout()功能(尽管显然是HTML5草案的一部分).此外,似乎有一个4-10ms事实上的最小粒度级别,所以这似乎是"它是如何".

开源软件的优点在于您可以根据需要提供补丁以包含更高的分辨率!


Ste*_*ein 1

为了完整起见,我想指出 NodeJS 的实现:

https://github.com/nodejs/node-v0.x-archive/blob/master/lib/timers.js#L214

这是:

// Timeout values > TIMEOUT_MAX are set to 1.
var TIMEOUT_MAX = 2147483647; // 2^31-1
...
exports.setTimeout = function(callback, after) {
    var timer;

    after *= 1; // coalesce to number or NaN

    if (!(after >= 1 && after <= TIMEOUT_MAX)) {
        after = 1; // schedule on next tick, follows browser behaviour
    }

    timer = new Timeout(after);
    ...
}
Run Code Online (Sandbox Code Playgroud)

记住这句话:

空闲超时

因为许多套接字通常具有相同的空闲超时,所以我们不会为每个项目使用一个超时观察程序。开销太大了。
相反,我们将对具有相同超时值和链接列表的所有套接字使用单个观察程序。

libev 手册中描述了该技术: http://pod.tst.eu/http: //cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts

我们经过same timeout value (1)这里。

实现Timer在这里:
https://github.com/nodejs/node-v0.x-archive/blob/master/src/timer_wrap.cc