For an upcoming project with node.js I need to perform various housekeeping tasks at periodic times. Specifically some tasks every millisecond, others every 20 ms (50 times per second) and still others every second. So I thought about using setInterval(), with funny results: many function calls were being skipped.
The benchmark I used is as follows:
var counter = 0;
var seconds = 0;
var short = 1;
setInterval(function() {
counter ++;
}, short);
setInterval(function() {
seconds ++;
log('Seconds: ' + seconds + ', counter: ' +
counter + ', missed ' +
(seconds * 1000 / short - counter));
}, 1000);
There is a long timer of one second and a short one that can be adjusted using the variable short, in this case 1 ms. Every second we print the difference between the number of expected ticks in the short cycle and the actual number of times the short counter was updated.
Here is how it behaves when the short timer is 1 ms:
2012-09-14T23:03:32.780Z Seconds: 1, counter: 869, missed 131
2012-09-14T23:03:33.780Z Seconds: 2, counter: 1803, missed 197
2012-09-14T23:03:34.781Z Seconds: 3, counter: 2736, missed 264
...
2012-09-14T23:03:41.783Z Seconds: 10, counter: 9267, missed 733
Many function calls are skipped. Here it is for 10 ms:
2012-09-14T23:01:56.363Z Seconds: 1, counter: 93, missed 7
2012-09-14T23:01:57.363Z Seconds: 2, counter: 192, missed 8
2012-09-14T23:01:58.364Z Seconds: 3, counter: 291, missed 9
...
2012-09-14T23:02:05.364Z Seconds: 10, counter: 986, missed 14
Better, but roughly one function call is skipped every second. And for 20 ms:
2012-09-14T23:07:18.713Z Seconds: 1, counter: 46, missed 4
2012-09-14T23:07:19.713Z Seconds: 2, counter: 96, missed 4
2012-09-14T23:07:20.712Z Seconds: 3, counter: 146, missed 4
...
2012-09-14T23:07:27.714Z Seconds: 10, counter: 495, missed 5
Finally for 100 ms:
2012-09-14T23:04:25.804Z Seconds: 1, counter: 9, missed 1
2012-09-14T23:04:26.803Z Seconds: 2, counter: 19, missed 1
2012-09-14T23:04:27.804Z Seconds: 3, counter: 29, missed 1
...
2012-09-14T23:04:34.805Z Seconds: 10, counter: 99, missed 1
In this case it skips very few calls (the gap increased to 2 after 33 seconds and to 3 after 108 seconds.
The numbers vary, but are surprisingly consistent between runs: Running the first 1 ms benchmark three times yielded a delay after 10 seconds of 9267, 9259 and 9253.
I have found no references for this particular problem. There is this much cited Ressig post and lots of related JavaScript questions, but most assume that the code runs in a browser and not in node.js.
Now for the dreaded question: what is going on here? Just joking; obviously function calls are being skipped. But I fail to see the pattern. I thought that the long cycles might be preventing the short ones, but it doesn't make any sense in the 1 ms case. Short cycle function calls are not overlapping since they just update a variable, and the node.js process is near 5% CPU even with a short cycle of 1 ms. Load average is high though, at about 0.50. I don't know why a thousand calls are stressing my system so much, though, since node.js handles many more clients perfectly; it must be true that setInterval() is CPU intensive (or I am doing something wrong).
An obvious solution is to group function calls using longer timers, and then run short cycle function calls many times to simulate a shorter timer. Then use the long cycle as a "broom wagon" that makes any calls missed in the lower intervals. An example: set up 20 ms and 1000 ms setInterval() calls. For 1 ms calls: call them 20 times in the 20 ms callback. For the 1000 ms call: check how many times the 20ms function has been called (e.g. 47), do any remaining calls (e.g. 3). But this scheme is going to be a bit complex, since calls may overlap in interesting ways; also it will not be regular although it may look like it.
The real question is: can it be done better, either with setInterval() or other timers within node.js? Thanks in advance.
SetInterval functions in javascript are not accurate. You should try to use a high resolution timer.Building accurate Timers in javascript
Look at this doc: http://nodejs.org/api/timers.html#timers_settimeout_callback_delay_arg
It is important to note that your callback will probably not be called in exactly delay milliseconds - Node.js makes no guarantees about the exact timing of when the callback will fire, nor of the ordering things will fire in. The callback will be called as close as possible to the time specified.
This happens because application code blocks the event loop. All timers and I/O events can be handled only on the nextTick.
You can see this behaviour with this code:
setInterval(function() {
console.log(Date.now());
for (var i = 0; i < 100000000; i++) {
}
}, 1);
Try to change iterations count and see results.
Ideally, the timer will be triggered exactly if the applications tick will last less than one ms. But this is not practicable in a real application.
The answer happens to be a combination of those given by Vadim and zer02, so I am leaving a write-up here. As Vadim said, the system cannot cope with too frequent updates, and adding some load to the system is not going to help. Or rather the runtime cannot cope; the system should be more than capable of firing the callback every millisecond if needed, but for some unexplained reason often it doesn't want to.
The solution is to use accurate timers, as zer02 commented. Do not be misled by the name; the mechanism used is the same setTimeout(), but the delay is adjusted depending on the time left until the timer should fire. So, if the time is over then the "accurate timer" will call setTimeout(callback, 0) which is run immediately. System load is, surprisingly, less than with setInterval(): about 2% of the CPU instead of 5%, in my very unscientific sample.
This simple function may come in handy:
/**
* A high resolution timer.
*/
function timer(delay, callback)
{
// self-reference
var self = this;
// attributes
var counter = 0;
self.running = true;
var start = new Date().getTime();
/**
* Delayed running of the callback.
*/
function delayed()
{
callback(delay);
counter ++;
var diff = (new Date().getTime() - start) - counter * delay;
if (!self.running) return;
setTimeout(delayed, delay - diff);
}
// start timer
delayed();
setTimeout(delayed, delay);
}
To use, just call new timer(delay, callback);. (Yes, I reversed the order of the parameters since having the callback first is very annoying.) To stop it, set timer.running = false.
One final note: setTimeout(callback, delay) does not use recursion as I feared (as in: wait for some time, then invoke the callback), it just places the callback in a queue which will be called by the runtime when its turn comes, in the global context.
I Disabled Debugger and tried again.It worked fine for me
Related
This code recursively calls the same function with a setTimeout of 1 millisecond, which in theory should call the function 1000 times per second. However, it's only called about 200 times per second:
This behavior happens on different machines and different browsers, I checked if it's has something to do with the maximum call stack, but this limit is actually way higher than 200 on any browser.
const info = document.querySelector("#info");
let start = performance.now();
let iterations = 0;
function run() {
if (performance.now() - start > 1000) {
info.innerText = `${iterations} function calls per second`;
start = performance.now();
iterations = 0;
}
iterations++;
setTimeout(run, 1);
}
run();
<div id="info"></div>
There’s a limitation of how often nested timers can run. The HTML5 standard says: 11: "If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4."
This is true only for client-side engines (browsers). In Node.JS this limitation does not exist
HTML Standard Timers Section
Very similar example from javascript.info
The delay argument passed to setTimeout and setInterval is not a guaranteed amount of time. It's the minimum amount of time you could expect to wait before the callback function is executed. It doesn't matter how much of a delay you've asked for, if the JavaScript call stack is busy, then anything in the event queue will have to wait.
Also, there is an absolute minimum amount of time you could reasonably expect a callback to be called after which is dependent on the internals of the client.
From the HTML5 Spec:
This API does not guarantee that timers will run exactly on schedule.
Delays due to CPU load, other tasks, etc, are to be expected.
I once read somewhere that it was around 16ms so setting a delay of anything less than that shouldn't really change the timings at all.
The costs of using Firebase cloud functions becomes greater when the amount of invocations becomes greater.
I was wondering if that means that I can reduce the cost when I invoke/run a function x amount of times in a function. I've tested this and it turned out that the invocations were reduced.
This is what I mean:
exports.functionName = functions.region("europe-west2").pubsub.schedule('every 1 minutes')
.onRun((context) => { //counts as invocation
console.log("Running...")
var timesRun = 0;
var interval = setInterval(() => {
timesRun += 1;
if(timesRun === 6){
console.log("Stopping interval...")
clearInterval(interval);
}
console.log("Executing...")
//code... for example fetching json
}, 10000); //doesn't count as invocation
});
With this, I can run my code 5 times a minute, while the official invocation is equal to 1.
Is this really more efficient, or am I missing something?
With this, I can run my code 5 times a minute, while the official invocation is equal to 1. Is this really more efficient, or am I missing something?
With Cloud Functions you pay for both invocation count and CPU/memory usage duration. So while your approach reduces the number of invocations, it increases the amount of time you're using the CPU/memory.
Which one comes out cheaper should be a matter of putting the data into the pricing calculator.
Note that while the calculator shows only full seconds, you're actually billed for compute time per 100ms:
Compute time is measured in 100ms increments, rounded up to the nearest increment. For example, a function executing for 260ms would be billed as 300ms.
According to the documentation, your function times out after 1 minute, extended to 9 minutes.
Whatever you do into that time, will count as 1 execution time.
if you start interacting with other services like Firestore, those operations are billed separated.
I want to execute a function every 100 milliseconds. it can be done using an interval like this:
setInterval(function(){
console.log('done')
}, 100);
But this takes 2.5 to 3 percent CPU usage on chrome.
Now I want to do the same thing using requestAnimationFrame to test the CPU usage and compare the performances.
How can I Call a function or console log every 100 milliseconds using requestAnimationFrame on chrome?
Here is same stack question but I can't make a working code: Call a function each x second in requestAnimationFrame
The cost you're seeing isn't primarily setInterval's internals, it's the work to call your callback and the work your callback is doing. That's not going to change for the better with requestAnimationFrame. Much more likely it'll get worse.
Here's how you'd do it¹, but I suspect you'll find you're better off doing what you're doing or not doing either of them:
var last = Date.now();
requestAnimationFrame(function tick() {
if (Date.now() - last >= 92) { // Why 92 instead of 100? See ¹ below.
doYourWorkHere();
last = Date.now();
}
requestAnimationFrame(tick);
});
Note that your function is now doing at least slightly more work, and being called much more often (every ~17ms instead of every ~100ms).
So: You're probably better off sticking with setInterval. If the CPU load of the work being done every 100ms is too high, you'll need to back off the interval.
¹ Why 92 instead of 100? Because each callback is about ~17ms after the last, so if you don't do your processing when it's been [say] 95ms since the last call, you won't get a chance until ~17ms later when it's been ~112ms since the last call. 92 is 100 - (17 / 2) rounded down, so at 91 you'll wait until 108, but at 92 you'll go right away. This helps you stay near every 100ms. Live example:
var counter = 0;
var last = Date.now();
requestAnimationFrame(function tick() {
let diff;
if ((diff = Date.now() - last) >= 92) {
console.log(`Time since last: ${diff}ms`);
last = Date.now();
++counter;
}
if (counter < 20) { // Just to keep the example from running forever
requestAnimationFrame(tick);
}
});
.as-console-wrapper {
max-height: 100% !important;
}
UPD: The question What is the reason JavaScript setTimeout is so inaccurate? asks why the timers in JavaScript are inaccurate in general and all mentions of the inaccuracy are about invocations slightly after the specified delay. Here I'm asking why NodeJS tolerates also invocations even before the delay? Isn't it an error-prone design of the timers?
Just found an unexpected (to me only?) behaviour of NodeJS setTimeout(). Some times it triggers earlier than the specified delay.
function main() {
let count = 100;
while (count--) {
const start = process.hrtime();
const delay = Math.floor(Math.random() * 1000);
setTimeout(() => {
const end = process.hrtime(start);
const elapsed = (end[0] * 1000 + end[1]/1e6);
const dt = elapsed - delay;
if (dt < 0) {
console.log('triggered before delay', delay, dt);
}
}, delay);
}
}
main();
On my laptop output is:
$ node --version
$ v8.7.0
$ node test.js
triggered before delay 73 -0.156439000000006
triggered before delay 364 -0.028260999999986325
triggered before delay 408 -0.1185689999999795
triggered before delay 598 -0.19596799999999348
triggered before delay 750 -0.351709000000028
Is it a "feature" of the event loop? I always thought that it must be triggered at least after delay ms.
From the NodeJS docs:
The callback will likely not be invoked in precisely delay milliseconds. Node.js makes no guarantees about the exact timing of when callbacks will fire, nor of their ordering. The callback will be called as close as possible to the time specified.
As you increase the number of intervals (you have 100) the accuracy decreases, e.g., with 1000 intervals accuracy is even worse. With ten it's much better. As NodeJS has to track more intervals its accuracy will decrease.
We can posit the algorithm has a "reasonable delta" that determines final accuracy, and it does not include checking to make sure it's after the specified interval. That said, it's easy enough to find out with some digging in the source.
See also How is setTimeout implemented in node.js, which includes more details, and preliminary source investigation seems to confirm both this, and the above.
From node timers doc, about settimeout:
The only guarantee is that the timeout will not execute sooner than the declared timeout interval
The code I wrote to call a function on the minute every minute, I think is flawed, as It's good for a while, but tends to lag behind by about 15 seconds for every hour since the page was loaded. To be honest I can't figure out what's causing the lagging, maybe it's the time it takes the functions to execute, small lapses all adding up and accumulating. Is there a way to auto-correct the lapses within the function as it's called. Or maybe someone knows a better method of achieving on the minute function calls. Any help or ideas much appreciated. Thanks.
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function setTimer() {
onTheMinFunc();
setTimeout(setTimer, delay);
}, start);
First of all, the DOM Timers API does not guarantee accuracy. I quote:
This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.
Second, you have a lag on each round caused by the time onTheMinFunc() is executed (you only set the timeout when it's done).
So, let's say onTheMinFunc takes half a second to execute - you get half a second delay at each minute and it accumulates - after only 10 minutes it'll lag quite a bit. (Note, functions should usually not take more than 15ms to execute anyway to avoid noticeable lag)
Try:
setInterval(onTheMinFunc, delay);
It still won't be very accurate. You can poll on much shorter intervals and keep track of a date variable - but again - no guarantees.
What you probably want is setInterval:
setInterval(onTheMinFunc, delay);
As is, your code using setTimeout means that the time it takes to execute your onTheMinFunc is being added into your delay before the next one is started, so over time, this extra delay will add up.
Using setInterval will be more accurate, since the delay is between calls to execute the function, rather than starting the timer only after the function is finished.
Timers and javascript times aren't very accurate, and I would think the only way to make sure a function is executed every whole minute over time, is to check the seconds every second
setInterval(function() {
if ( new Date().getSeconds() === 0 ) onTheMinFunc();
},1000);
FIDDLE
Here is a slight modification to your code:
function everyMinute(fn) {
arguments[1] && fn();
var now = new Date();
var delay = 60 * 1000 - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function(){
everyMinute(fn, true);
}, start);
}
everyMinute(onTheMinFunc);
It recalculates the number of milliseconds to wait till the next minute every time so it is as accurate as possible to the top of the minute.
I think you want something closer to this:
function setNextMinute() {
// figure out how much time remains before the end of the current minute
var d = new Date().getTime()%60000;
//set a timeout to occur when that expires.
setTimeout(function () {
// recalculate a new timeout so that your timer doesn't lag over time.
doWhateverYouWantToHere();
// note that calling doWhateverYouWantToHere() will
// not offset the next minute, since it is recalculated in setNextMinute()
setNextMinute();
},60000-d);
}
setNextMinute();
caveat: I did not thoroughly test this for timing. But it appeared to work for 1 sec intervals and 1 min intervals well enough.
This has the advantage of not recalculating every second, and also not just starting a 60 second timer from whatever the current time is.
The current accepted answer may overkill
Executing if ( new Date().getSeconds() === 0 ) onTheMinFunc(); on each second (and forever) seems to not be a good idea.
I will not benchmark it against the following propositions, it's not necessary.
Clues
Use whatever logic is necessary to calculate the start moment.
On the start moment
Use setInterval for remaning executions
Execute the first call
Note setInterval is called ASAP to avoid that time lapses.
If you want that new Date().getSeconds() === 0:
var id = setInterval(function() {
if ( new Date().getSeconds() === 0 ) {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
clearInterval(id);
}
},1000);
Alternatively, you could use your own logic:
var now = new Date();
var delay = 60 * 1000; // 1 min in msec
var start = delay - (now.getSeconds()) * 1000 + now.getMilliseconds();
setTimeout(function() {
setInterval(onTheMinFunc, delay);
onTheMinFunc();
}, start);
Please check both examples working on jsfiddle
The second (Example B) seems more accurate.