我将用于JavaScript的Node.js代码(v10.13.0)重构为异步/等待异步代码,该代码以前是同步的。后来我注意到,性能下降了~3倍的程序执行时间。
当将同步函数调用链转换为异步函数调用时,是否存在性能损失?
简化示例
变更同步码
function fn1() {
return 1;
}
function fn2() {
return fn1();
}
(function() {
const result = fn2();
})();转换为异步代码:
async function fn1() {
return 1;
}
async function fn2() {
return await fn1();
}
(async function() {
const result = await fn2();
})();有什么事件循环魔法可以使后一段代码在Node.js webapp中变慢吗?
发布于 2021-12-10 20:25:52
下面是一个更高级的基准,它使用同步或异步函数计算Fibonacci系列:
async function benchmark(M = 1000000, N = 100) {
function fibonacci_sync(num) {
let a = 1, b = 0, temp
while (num >= 0) {
temp = a; a = a + b; b = temp; num--
}
return b
}
async function fibonacci_async(num) {
let a = 1, b = 0, temp
while (num >= 0) {
temp = a; a = a + b; b = temp; num--
}
return b
}
timeitSync ('sync', M, () => {for(let i = 0; i < N; i++) fibonacci_sync(i)})
await timeit('async', M, async () => {for(let i = 0; i < N; i++) await fibonacci_async(i)})
}node.js中的示例执行时间证明是2.8x慢
sync: 4.753s
async: 13.359s对于更大的M,但是更小的N = 10而不是N=100 (更短的计算,所以等待会产生更大的影响),异步函数变成14.5x慢 (ooops!!):
sync: 0.499s
async: 7.258s这是在Node v16.13.1上。基准测试的灵感来自于这篇文章:https://madelinemiller.dev/blog/javascript-promise-overhead/
为了完整起见,下面是上面使用的timeit函数:
async function timeit(label, repeat, fun) {
console.time(label)
for (let i = 0; i < repeat; i++) await fun()
console.timeEnd(label)
}
function timeitSync(label, repeat, fun) {
console.time(label)
for (let i = 0; i < repeat; i++) fun()
console.timeEnd(label)
}当使用async timeit而不是timeitSync度量async timeit时,在最后一个示例中,执行时间从0.499s增长到1.2s,这再次证实了async带来了很大的减速。
因此,是的,的确,异步调用可能带来性能的巨大下降,甚至是数量级的下降。每个调用都必须经过一个事件队列,其管理似乎会造成大量开销。在实现包含大量异步函数的代码时,一定要考虑到这一点。
考虑到async范式的“传染性”--一个低级函数是异步的,树上的所有调用者也必须是异步的--我很高兴JS引入了优化,允许在某些情况下(大多数情况下)立即执行await...,而不是被一次又一次地推到队列中,每两条指令一次又一次。这可能会使所有场景受益,在这些场景中,await被包装在条件中,很少需要实际停止函数,但仍然需要将函数声明为“异步”,不管“等待”到达的频率有多高。
https://stackoverflow.com/questions/55603430
复制相似问题