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

时间:2012-02-15 04:52:20

标签: node.js v8

在处理时间敏感项目时,我使用下面的代码来测试可用的计时事件的粒度,首先在我的桌面计算机上测试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&lt; 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();

2 个答案:

答案 0 :(得分:5)

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

  

重要的是要注意你的回调可能不会在几毫秒内被调用 - Node.js使无法保证回调何时的确切时间火,也不会触发命令。回调将尽可能接近指定时间。

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

[编辑] 顺便提一下,setTimeout() function似乎没有任何规范强制要求(尽管apparently part of the HTML5 draft)。此外,似乎有一个4-10ms的事实上最小粒度级别,所以这似乎是“它是如何”。

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

答案 1 :(得分: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);
    ...
}

记住这句话:

  

IDLE TIMEOUTS

     

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

     

这项技术在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