Allow running setInterval more than once per millisecond in nodejs
Asked Answered
P

6

20

I have a node script which is supposed to utilize all the CPU resources a single node process could get. But I found setInterval to be too slow.

And sure enough I found this in the documentation:

When delay is larger than 2147483647 or less than 1, the delay will be set to 1.

source: https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args

Now I wonder if there is a way to reduce the limit further or if there is an alternative function that I could use.

I can't just use a normal loop because there are other asynchronous things that need to run at the same time.

Edit:
Again: I can't just use a normal loop because there are other asynchronous things that need to run at the same time. I'm not sure why this is so hard to understand.

While a normal loop is running, you are blocking the execution of everything else. It doesn't matter if you put the loop in another asynchronously executed function.

What does this mean?

Let's look at some examples:

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background

while (true) {
    // do whatever
}

What will this code do? It will block everything. console.log('a') will not be executed continuously.

setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background
setTimeout(()=>{
    while (true) {
        // do whatever
    }
}, 1)

This will also block the execution of the intervals as soon as the while loop starts.

Psychasthenia answered 22/1, 2018 at 11:3 Comment(3)
Why do you want to put it in a setInterval then? Just put it in an infinite for or while loop!Gas
Can you post your current code?Barbirolli
Because Node is single threaded. So async is more like interrupt.Casiecasilda
D
8

I believe the question belongs to node rather than to browser. You can use some of the following options (recursively/in loop) for reducing your delay time.

setImmediate

setImmediate - Schedules the "immediate" execution of the callback after I/O events' callbacks. Returns an Immediate for use with clearImmediate().

When multiple calls to setImmediate() are made, the callback functions are queued for execution in the order in which they are created. The entire callback queue is processed every event loop iteration. If an immediate timer is queued from inside an executing callback, that timer will not be triggered until the next event loop iteration.

It's from node guides:

setImmediate and setTimeout are similar, but behave in different ways depending on when they are called.

  • setImmediate() is designed to execute a script once the current poll phase completes.
  • setTimeout() schedules a script to be run after a minimum threshold in ms has elapsed.

process.nextTick

The process.nextTick() method adds the callback to the "next tick queue". Once the current turn of the event loop turn runs to completion, all callbacks currently in the next tick queue will be called.

From node guide

We recommend developers use setImmediate() in all cases because it's easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)

Damian answered 24/1, 2018 at 12:48 Comment(0)
H
4

1 setInterval multiple times run more!

let count = 0,
  by = 100,
  _intervals = [],
  timelimit = 100
for (let i = 0; i < by; i++) {
  _intervals[i] = setInterval(() => count++, 1)
}
setTimeout(() => {
  _intervals.forEach(x => clearInterval(x))
  console.log(`count:${count}`)
}, timelimit)

enter image description here

2.setTimeout recurser run less!

let count = 0,
  go = true
recurser()
setTimeout(() => {
  go = false
  console.log(`count:${count}`)
}, 100)

function recurser() {
  count++
  go && setTimeout(recurser)
}
enter image description here

3.requestAnimationFrame run less!

let count = 0,
  go = true,
  timelimit = 100
step()
setTimeout(() => {
  go = false,
    console.log(`count:${count}`)
}, timelimit)

function step() {
  count++
  go && requestAnimationFrame(step)
}
enter image description here

so as I know ,run setInterval multiple times, and I believe while will count more

Horacehoracio answered 24/1, 2018 at 12:13 Comment(0)
P
4

Thanks to Josh Lin for the idea to just run multiple intervals. I ended up with two simple wrapper functions for setInterval and clearInterval:

function setInterval2(cb,delay) {
    if (delay >= 1)
        return [setInterval(cb,delay)];
    var intervalArr = [];
    var intervalCount = Math.round(1/delay);
    for (var i=0; i<intervalCount; i++)
        intervalArr.push(setInterval(cb,1));
    return intervalArr
}

function clearInterval2(intervalArr) {
    intervalArr.forEach(clearInterval);
}

It works just like the original functions:

var count = 0;

// run interval every 0.01 milliseconds:
var foo = setInterval2(function(){
    count++;
},0.01);

// stop execution:
clearInterval2(foo)
Psychasthenia answered 24/1, 2018 at 12:37 Comment(0)
C
1

You ask if it is possible to

run setInterval more than once per millisecond in nodejs

As you note in your question, this is not possible with setInterval, since there is always a minimum delay of at least 1 ms in node.js. In browsers, there is often a minimum delay of at least 10 ms.

However, what you want to achieve—to repeatedly run CPU-intensive code without unnecessary delay—is possible in other ways.

As noted in the answer by The Reason, setImmediate is a good option available in node.js. As setImmediate has limited browser support, and is unlikely to be widely supported in the future, there is another approach that also works in browsers.

While browsers enforce a minimum delay for setInterval and setTimeout, the delay for setTimeout is enforced from when the timer is set, not when it is run. If we use setTimeout repeatedly to call CPU-intensive code, we can make sure that a timer is always set 10–15 ms in advance (if the code takes at least 10–15 ms to run), thus reducing the actual delay to 0 ms.

The demo snippet below borrows code from this answer to demonstrate how the use of timers set in advance may make the delay smaller than what is enforced. In the browsers I tested, this usually results in a 0 ms delay.

// First: repeat runCPUForAtLeast50ms() 10 times
// with standard repeated setTimeouts and enforced delays
testTimeout(10, function(){
  // Then: repeat runCPUForAtLeast50ms() 10 times
  // using a repeated set of queued setTimeouts
  // circumventing the enforced delays
  testTimeout(10, false, true);
});

function testTimeout(repetitions, next, multiple) {
  var delays = [];
  var lastCheck;
  var extraTimers;

  function runner() {
    if(lastCheck){
      delays.push((+new Date) - lastCheck);
    }
    if(repetitions > 0) {
      //process chunk
      runCPUForAtLeast50ms();
      //set new timer
      setTimeout(runner);
    } else if(repetitions === 0) {
      //report result to console
      console.log((multiple? 
        'Repeated multiple timers delays: ' : 
        'Repeated single timer delays: ') + delays.join(', '));
      //invoke next() function if provided
      next && next();
    }
    repetitions--;
    lastCheck = +new Date;
  }

  setTimeout(runner);

  if(multiple){
   // make sure that there are always a fixed
   // number of timers queued by setting extra timers
   // at start
   extraTimers = 10;
   while(extraTimers--)
     setTimeout(runner);
  }
}

function runCPUForAtLeast50ms() {
  var d = (+new Date) + 50;
  while(+new Date < d);
}
Changeup answered 29/1, 2018 at 13:25 Comment(0)
S
0

I think you can solve your problem using async module... a way could be:

async.parallel([
  (callback) => {
    // do normal stuff
  },
  (callback) => {
    // do your loop
  }
], (err, results) => {
  // ...
});

But take into account this note from the official documentation...

Note: parallel is about kicking-off I/O tasks in parallel, not about parallel execution of code. If your tasks do not use any timers or perform any I/O, they will actually be executed in series. Any synchronous setup sections for each task will happen one after the other. JavaScript remains single-threaded.

Schnabel answered 30/1, 2018 at 9:52 Comment(0)
C
0

In short answer, you can't. There are some limitation of Javascript/Node as it is a single thread application. That's why you have async interrupts.

The long answer: From the computer architecture's perspective, modern CPU and Kernel scheduling is not deterministic. If you want such fine grain control, I would suggest you look at MCU and embedded solutions that does not have a kernel scheduler. Because, your OS have many other processes and Kernel processes that takes up CPU time, so kernel scheduler have to keep scheduling different processes to be run on the CPU and meet many different demands.

Even with the set 1ms, when you try to measure, it is probably not 1ms (exact time depends on OS, hardware and amount of processes running on your computer).

Now, if you want to use all CPU resources, not possible.

But if you want to utilize as much as resources as possible, you can explore current programming pattern. For example, you can schedule 1 million threads (you machine probably won't be able to handle it), or some crazy large amount of processes and let your scheduler to be constantly putting process on your CPU, so there is no idle time and max out CPU utilization.

Alternatively, you can run CPU Stress tests, and those are designed to simply max out your CPU and keep it burning to high temperature -- make sure you have the cooling solution in place.

Casiecasilda answered 30/1, 2018 at 17:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.