为什么混杂会导致循环花费更长的时间?

时间:2019-12-10 19:46:20

标签: node.js asynchronous promise node-promisify

我有一个必须从视频中提取颜色信息的应用程序,它通过分析每个帧来做到这一点。首先,我提取帧,然后将其位置的数组加载到内存中。就像您想象的那样,即使是一小段视频,它也可以成千上万。

我用来提取每个帧颜色信息的函数是一个Promise,因此我选择使用Promise.all

批处理一个Promise数组。

对于每个文件的绝对路径,我使用fs读取文件,然后将其传递以进行处理。我已经使用许多独立的图像完成了此操作,并且知道该过程仅花费大约一秒钟的时间,但是突然间,它花费了将近20分钟的时间来处理一张图像。我终于发现,在promisify上使用fs.readFile是造成瓶颈的原因。我不明白为什么?

在第一个fs.readFile中,它会在返回的承诺中进行转换,而在第二个fs.readFile中,它通常会像往常一样使用,我等待被调用的决心。我不介意使用不承诺的服务,只是很好奇为什么这会导致速度变慢?

我停止使用promisify的应用每秒加速回到1帧/秒

慢速代码:

   async analyzeVideo(){
    await this._saveVideo();
    await this._extractFrames();
    await this._removeVideo();

    const colorPromises = this.frameExtractor.frames.map(file => {
      return new Promise(resolve => {
        //transform image into data
        const readFile = promisify(fs.readFile);
        readFile(file)
            .then(data => {
              const analyzer = new ColorAnalyzer(data);
              analyzer.init()
                  .then(colors => {
                    resolve(colors)
                  })
            })
            .catch((e)=> console.log(e));
      })
    });
    const colors = await runAllQueries(colorPromises);

    await this._removeFrames();

    this.colors = colors;

    async function runAllQueries(promises) {
      const batches = _.chunk(promises, 50);
      const results = [];
      while (batches.length) {
        const batch = batches.shift();
        const result = await Promise.all(batch)
            .catch(e=>console.log(e));
        results.push(result)
      }
      return _.flatten(results);
    }
  }

快速代码:

async analyzeVideo(){
    await this._saveVideo();
    await this._extractFrames();
    await this._removeVideo();
    const colorPromises = this.frameExtractor.frames.map(file => {
      return new Promise(resolve => {
        //transform image into data
        fs.readFile(file, (err, data) => {
          const analyzer = new ColorAnalyzer(data);
          analyzer.init()
              .then(colors => {
                resolve(colors)
              })
        });
      })
    });
    const colors = await runAllQueries(colorPromises);

    await this._removeFrames();

    this.colors = colors;

    async function runAllQueries(promises) {
      const batches = _.chunk(promises, 50);
      const results = [];
      while (batches.length) {
        const batch = batches.shift();
        const result = await Promise.all(batch)
            .catch(e=>console.log(e));
        results.push(result)
      }
      return _.flatten(results);
    }
  }

1 个答案:

答案 0 :(得分:1)

您不需要在每次循环迭代中promisify,只需在模块顶部执行一次即可。

该问题很可能是由从未解决的承诺引起的。您没有正确处理错误,因此如果引发错误,Promise.all可能永远无法完成。

除了记录.catch中的错误外,您也必须reject,或者至少resolve也不关心错误。同样也不会捕获analyzer.init()错误(如果该函数可以拒绝)

const readFile = promisify(fs.readFile);
// ...

const colorPromises = this.frameExtractor.frames.map(file => {
   return new Promise((resolve, reject) => {
        //transform image into data
        // const readFile = promisify(fs.readFile);
        readFile(file)
            .then(data => {
              const analyzer = new ColorAnalyzer(data);
              return analyzer.init()   
            })
            .then(resolve) // colors
            .catch((e)=> { 
              reject(e);
              console.log(e)
            });
      })
})

除此之外,runAllQueries并没有按照您认为的去做。您已经履行了所有诺言。

我建议您改用p-limit

const pLimit = require('p-limit');

const limit = pLimit(50);

/* ... */

const colorPromises = this.frameExtractor.frames.map(file => {
   return limit(() => {
        return readFile(file)
            .then(data => {
              const analyzer = new ColorAnalyzer(data);
              return analyzer.init()   
            })
            .then(resolve) // colors
    })
})

const colors = await Promise.all(colorPromises);

此外,如果您一次执行50次读取,则应增加UV_THREADPOOL_SIZE的值,该值默认为4。

在您进入之前,没有任何要求:

process.env.UV_THREADPOOL_SIZE = 64 // up to 128

或将该脚本称为:UV_THREADPOOL_SIZE=64 node index.js