nodejs 异步io加速3倍

旧城冷巷雨未停 提交于 2020-02-27 04:58:05

简化需求

需要读取很多文件, 将其中内容读出后, 获取所有文件内容总长度

数据表现有一些波动是因为笔记本电压或后台影响,  但是优化效果是一致的

将2500ms的任务优化到400ms左右

 

最简单的同步读

function getLen(x) {
  return fs.readFileSync(path.join(root, x, x + '.json'), 'utf8').length
}

function run() {
  let st = +new Date()
  let sum = names.reduce(
    (pre, cur) => pre + getLen(cur)
    , 0
  )
  console.log(+new Date() - st, sum)
}

for (let i = 0; i < 10; i++)
  run()

两万个文件, 基本上都是2500ms

2860 18423803
2516 18423803
2501 18423803
2513 18423803
2502 18423803
2525 18423803
2478 18423803
2490 18423803
2586 18423803
2610 18423803

 

简单单个异步

迭代器和rxjs使用异步读, 能快30% 左右,  使用异步的文件时, 有文件数目的最大限制, 而且同时多异步文件数目测试中16个左右就到了极限, 可能是我这边的文件比较小, 都是2~10k, 可能在大文件上会有优势吧


function* getName() {
  for (let x of names)
    yield path.join(root, x, x + '.json')
}

function run() {
  let subject = new Subject()
  let g = getName()
  let sum = 0
  let cnt = 0
  let st = +new Date()
  subject.subscribe(
    x => {
      fs.readFile(x.value, 'utf8', (err, data) => {
        cnt++
        sum += data.length
        if (cnt === names.length) {
          console.log('end', +new Date() - st, sum)
          return
        }
        subject.next(g.next())
      })
    }
  )
  subject.next(g.next())
}


setInterval(
  run, 3000
)

// end 1835 18423803
// end 1771 18423803
// end 1761 18423803
// end 1741 18423803

 

多个异步Promise

使用promise多个, 为了避免v8 优化, 正反跑一边, 如果每次只执行一个异步流, 那么就会退化到简单异步,  时间也是两秒多, 也能看到目前的需求下, 增加个数只会在一开始有极大提升, 后面几乎没有太多作用

不过效果已经提高了四倍多

let st = +new Date()
const names = fs.readdirSync(root).slice(0, 20000)

function getLen(x) {
  return new Promise(
    resolve => {
      let p = path.join(root, x, x + '.json')
      fs.readFile(p, 'utf8', (err, data) => {
        resolve(data.length)
      })
    }
  )
}


async function arr(n = 1) {
  let arr = []
  let sum = 0
  let cnt = 0
  for (let i of names) {
    cnt++
    arr.push(i)
    if (arr.length === n || cnt === names.length) {
      // console.log(n, cnt, arr.length, names.length)
      let res = await Promise.all(arr.map(i => getLen(i)))
      sum += res.reduce((pre, cur) => pre + cur, 0)
      arr = []
    }
  }
  let ed = +new Date()
  console.log('sum', n, sum, ed - st)
  return ed - st
}

async function start() {
  let t = []
  // for (let i = 127; i > 0; i -= 2) {
  for (let i = 1; i < 32; i += 2) {
    st = +new Date()
    t.push(await arr(i))
  }
  console.log(t.join(','))
}

start()
// sum 1 18423803 1817
// sum 3 18423803 736
// sum 5 18423803 460
// sum 7 18423803 467
// sum 9 18423803 394
// sum 11 18423803 406

 

多异步优化

上面的多异步有一个小的优化点, 比如一次执行n个异步, Promise.all 的时间是n个异步最长的时间, 每次保持异步队列长度固定, 一旦有一个异步执行完成, 则重新加一个进去, 大概能再优化几毫秒


const root = 'D:/data/扇贝单词/word_json_v2'
const names = fs.readdirSync(root).slice(0, 20000)

function getLen(x) {
  return new Promise(
    resolve => {
      let p = path.join(root, x, x + '.json')
      fs.readFile(p, 'utf8', (err, data) => {
        resolve(data.length)
      })
    }
  )
}

function* getName() {
  for (let i of names)
    yield i
}

let g = getName()
let size = 16
let sum = 0
let cnt = 0

function summary() {
  let next = g.next()
  if (next.done) {
    return
  }

  getLen(next.value).then(
    (data) => {
      sum += data
      cnt++
      if (cnt === names.length) {
        console.log('===', +new Date() - st, sum)
        return
      }
      summary()
    }
  )
}

let st = +new Date()
for (let i = 0; i < size; i++) {
  summary()
}


// === 377 18423803

 

总结

通过使用rxjs, 生成器, promise 等方法做这次优化, 也算是对这些技术点有了更多的了解, 以后要多思考~

 

 

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!