为什么读取 10 个 50 MB 文件所需的时间与在 nodejs 中读取一个 500 MB 文件所需的时间相同?

Why does reading 10 50 MB files take the same amount of time as reading one 500 MB file in nodejs?

提问人:Alexander Cheprasov 提问时间:9/22/2023 更新时间:9/22/2023 访问量:33

问:

// fulfill one promise 5000ms
console.time('test');

(async function () {
  await new Promise((resolve) => setTimeout(resolve, 5000));
  console.log('slept')
})()
  .then(() => {
    console.timeEnd('test');
  })
// Time ~5000 мс
// fulfill 10 promises by 500ms
console.time('test');

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let test = [];

(async function () {
  test = await Promise.all(
    arr.map((elem) => new Promise(async (resolve) => setTimeout(resolve, 500)))
  )
  console.log(test.length)
  console.log('raided')
})()
  .then(() => {
    console.timeEnd('test');
  })

// Time ~500 мс
// Read file 500mb
const fs = require('node:fs');

console.time('test');

let test;

(async function () {
  test = fs.readFileSync('./assets/500mb');
  console.log('raided', test.length)
})()
  .then(() => {
    console.timeEnd('test');
  })
// Time ~300 мс
// Read 10 files by 50mb
const fs = require('node:fs');

console.time('test');

const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

let test = [];

(async function () {
  test = await Promise.all(
    arr.map((elem) => new Promise((resolve) => fs.readFile(`./assets/50mb_${elem}`, resolve)))
  )

  console.log(test.length)
  console.log('raided')
})()
  .then(() => {
    console.timeEnd('test');
  })
// Time ~300ms

执行完前两个代码后,我们将得到大约 10 倍的执行速度差异。这通常是可以理解的。但是,如果我们读取文件而不是 setTimeout,则不会获得这种增加。为什么?充分解释,我知道读取文件不是由节点完成的,但是那里发生了什么?为什么我们不能获得至少 2 倍的增长?第三和第四个代码的时间是相同的,无论是读取一个 500 MB 的文件还是 10 个 50 MB 的文件都没有区别。为什么?

我试图自己弄清楚,但我无法弄清楚。

node.js 异步 promise readfile node.js-fs

评论

2赞 Scott Hunter 9/22/2023
因为两者都必须实际传输 500MB 的数据,不管你如何切碎它?
0赞 jfriend00 9/23/2023
这里有很多不对劲的地方。为什么要包装在函数中。这完全没有意义。如果您不想使用 promises,请使用 .如果要使用同步 I/O,请不要将其包装在 promise 中。fs.readFileSync()asyncfs.promises.readFile()
0赞 Alexander Cheprasov 9/23/2023
问题不在于代码的正确性,而在于思想本身和理解的深度。我看到的所有解释都是抽象的。没有人能在流程和硬件层面上解释正在发生的事情。nodejs 如何将命令传递给操作系统,操作系统以什么顺序处理它。除了从磁盘读取的速度之外,我们目前还受到硬件的限制。
0赞 Martheen 9/23/2023
对于区区 500 MB 的文件,您需要记住,大多数设备中的磁盘缓存已经足以完全处理它们,因此您实际上不是在处理磁盘 I/O,而只是在处理 RAM。

答:

0赞 Quentin 9/22/2023 #1

setTimeout一段时间后调用函数。在调用和调用回调之间的时间里,它不会做任何工作。它只是偶尔检查时钟。setTimeout

readFile读取文件。当文件被读入内存时,就会调用回调。读取文件是工作。计算机只能如此快速地从磁盘中提取数据。它不能并行读取多个文件,同时为每个文件保持与一次读取一个文件相同的每秒字节数。由于是异步的,它不会阻塞 JS 事件循环,因此 JS 程序可以同时执行其他操作(例如更新显示或通过网络发送消息),但是计算机的磁盘读取部分是任何想要从磁盘读取的东西的瓶颈。readFile


如果你喜欢一个类比:

setTimeout就像在说“一小时后敲响那个铃铛”。你可以坐在椅子上等待。

readFile就像说“去学校接孩子,然后叮那个铃铛”,也“去商店买牛奶,然后叮那个铃铛”,也像是“去公园遛神,然后叮那个铃铛”。这些任务中的每一个都需要 1 小时,但您将很难在一小时内完成所有任务。您需要 3 小时才能完成所有 3 个。

评论

0赞 Alexander Cheprasov 9/23/2023
谢谢你的回答,但它也很抽象。我正在寻找一个更深层次的答案,敲响铃铛对我来说是有意义的。但是,从 10 个承诺到与磁盘通信的驱动程序的流程很有趣。我假设密钥在线程或处理器内核中的某个地方。该过程在一个内核上启动,因此对磁盘的所有操作都将在一个内核(线程)上进行,我们不会进行并行工作。但这些都是假设,没人能说出来。
0赞 Quentin 9/23/2023
@AlexanderCheprasov — 不,这是硬件问题。