我有一个节点脚本,它应该利用单个节点进程可以获得的所有CPU资源。但是我发现setInterval太慢了。
我确实在文档中找到了这个:
当延迟大于2147483647或小于1时,延迟将为 设为1.
来源:https://nodejs.org/api/timers.html#timers_setinterval_callback_delay_args
现在我想知道是否有办法进一步减少限制,或者是否有可以使用的替代功能。
我不能只使用普通循环,因为还有其他需要同时运行的异步事件。
修改
再说一遍:我不能只使用普通循环,因为还有其他需要同时运行的异步事件。
我不确定为什么这么难理解。
当正常循环正在运行时,您阻止执行其他所有操作。如果将循环放在另一个异步执行的函数中并不重要。
这是什么意思?
让我们看一些例子:
setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background
while (true) {
// do whatever
}
这段代码会做什么?它会阻止一切。 console.log('a')
不会被连续执行。
setInterval(()=>{console.log('a')},1000) // asynchronous thing that needs to run in the background
setTimeout(()=>{
while (true) {
// do whatever
}
}, 1)
这也会在while循环开始时阻止执行间隔。
答案 0 :(得分:8)
我认为这个问题属于 node 而不是浏览器。您可以使用以下某些选项(递归/循环)来减少延迟时间。
使用
setImmediate
- 在I / O事件的回调之后安排“立即”执行回调。返回立即值以供clearImmediate
()。当对setImmediate()进行多次调用时,回调函数按照创建它们的顺序排队等待执行。每次事件循环迭代都会处理整个回调队列。如果立即计时器从执行的回调内部排队,则在下一次事件循环迭代之前不会触发该计时器。
来自node
指南:
setImmediate
和setTimeout
相似,但行为不同 取决于何时被召唤的方式。
setImmediate()
用于在当前轮询阶段完成后执行脚本。setTimeout(
)安排在经过最小阈值(ms)后运行的脚本。
process.nextTick()
方法将回调添加到“下一个刻度” 队列“。一旦事件循环的当前转弯转到 完成后,当前在下一个tick队列中的所有回调都将是 调用。
来自node
指南
我们建议开发人员在所有情况下都使用setImmediate(),因为它是 更容易推理(它导致代码与a兼容) 更广泛的环境,如浏览器JS。)
答案 1 :(得分:3)
1 setInterval
次运行更多!
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)
2. setTimeout
递归少跑!
let count = 0,
go = true
recurser()
setTimeout(() => {
go = false
console.log(`count:${count}`)
}, 100)
function recurser() {
count++
go && setTimeout(recurser)
}
3.requestAnimationFrame 少跑!
let count = 0,
go = true,
timelimit = 100
step()
setTimeout(() => {
go = false,
console.log(`count:${count}`)
}, timelimit)
function step() {
count++
go && requestAnimationFrame(step)
}
据我所知,多次运行setInterval
,我相信while
会更多
答案 2 :(得分:3)
感谢Josh Lin想要运行多个间隔。我最终得到了setInterval
和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);
}
它的工作方式与原始功能相同:
var count = 0;
// run interval every 0.01 milliseconds:
var foo = setInterval2(function(){
count++;
},0.01);
// stop execution:
clearInterval2(foo)
答案 3 :(得分:1)
你问是否可以
在nodejs中每毫秒运行一次setInterval
正如您在问题中所述,setInterval
无法做到这一点,因为始终有minimum delay of at least 1 ms in node.js。在浏览器中,通常会有minimum delay of at least 10 ms。
但是,你想要实现的目的 - 在没有不必要的延迟的情况下重复运行CPU密集型代码 - 可以通过其他方式实现。
正如The Reason的回答所述,setImmediate
是node.js中的一个很好的选择。由于setImmediate
的浏览器支持有限,并且unlikely to be widely supported in the future,因此还有另一种方法可以在浏览器中使用。
虽然浏览器强制执行setInterval
和setTimeout
的最小延迟,但setTimeout
的延迟是在设置计时器时强制执行的,而不是在运行时执行的。如果我们反复使用setTimeout
来调用CPU密集型代码,我们可以确保计时器总是提前10-15毫秒设置(如果代码运行至少需要10-15毫秒),从而减少了实际延迟到0毫秒。
下面的演示片段借用了来自this answer的代码,以演示如何使用预先设置的计时器可以使延迟小于强制执行的延迟。在我测试的浏览器中,这通常会导致0 ms的延迟。
// 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);
}
&#13;
答案 4 :(得分:0)
我认为您可以使用async模块来解决您的问题......方法可能是:
async.parallel([
(callback) => {
// do normal stuff
},
(callback) => {
// do your loop
}
], (err, results) => {
// ...
});
但请考虑官方文件中的这一说明......
注意:parallel是关于并行启动I / O任务,而不是关于 并行执行代码。如果您的任务不使用任何计时器或 执行任何I / O,它们实际上将被串行执行。任何 每个任务的同步设置部分将在一个之后发生 其他。 JavaScript仍然是单线程的。
答案 5 :(得分:0)
简而言之,你不能。由于它是单线程应用程序,因此Javascript / Node存在一些限制。这就是你有异步中断的原因。
答案很长: 从计算机体系结构的角度来看,现代CPU和内核调度不是确定性的。如果你想要这样精细的颗粒控制,我建议你看一下没有内核调度程序的MCU和嵌入式解决方案。因为,您的操作系统有许多其他进程和内核进程占用CPU时间,因此内核调度程序必须不断调度在CPU上运行的不同进程并满足许多不同的需求。
即使设置为1ms,当您尝试测量时,它可能不是1ms(具体时间取决于操作系统,硬件和计算机上运行的进程数量)。
现在,如果要使用所有CPU资源,则无法实现。
但是如果你想尽可能多地利用资源,你可以探索当前的编程模式。例如,您可以安排100万个线程(您的机器可能无法处理它),或者一些疯狂的大量进程,让您的调度程序不断地将进程放在您的CPU上,因此没有空闲时间和最大化CPU利用率。
或者,您可以运行CPU压力测试,这些测试旨在简化CPU并使其保持高温 - 确保您已准备好冷却解决方案。