How to improve puppeteer startup performance during tests

seasick picture seasick · Oct 21, 2019 · Viewed 7k times · Source

I've written a small crawler with the help of Puppeteer. Now I'm facing the challenge that my tests are rather slowly (> 3 seconds for each test). I've been able to track it down to the launch function of Puppeteer and the usage of Istanbul/nyc.

  • If I run the test just with mocha, the tests are finished under 400 ms.
  • But if I additionally use nyc, the duration of the tests exceeds 3000 ms

All that I'm using is

'use strict';
const puppeteer = require('puppeteer');


module.exports = async function startBrowser() {
  const options = {
    args: [
      // '--no-sandbox',
      // '--disable-setuid-sandbox',
      // '--disable-dev-shm-usage',
      // '--disable-accelerated-2d-canvas',
      // '--disable-gpu'
    ],
    headless: false // true
  };

  return await puppeteer.launch(options);
};

Here is the test I'm using:

'use strict';
/* global describe: false, before: false, it: false,
    beforeEach: false, afterEach: false, after: false, window: false, document: false */

const assert = require('assert').strict;
const startBrowser = require('../');
const util = require('util');



describe('Puppeteer', function() {
  let pageManager;

  it('start the browser', async function() {
    this.timeout(10000);

    console.time('startBrowser');
    const browser = await startBrowser();
    console.timeEnd('startBrowser');
    assert(browser);

    console.time('closeBrowser');
    await browser.close();
    console.timeEnd('closeBrowser');
  });

});

I've created a repository with this code and test here. nyc _mocha ./test/*.test.js runs in ~3500ms, mocha ./test/*.test.js takes only 130ms.

What I've tried so far:

  • different combination of include/exclude nyc flags
  • updating to latest versions of Puppeteer, nyc and mocha
  • removing my Puppeteer arguments
  • searching for Puppeteer & Istanbul related issues (with not much success)
  • trying headless: true
  • bypassing all proxies, see this puppeteer issue

What can I do to have tests with coverage be as fast as the tests alone?

Using:

  • Ubuntu 19.04
  • node.js 10.15.3

Answer

seasick picture seasick · Oct 28, 2019

I've started to debug Puppeteer and these are my findings:

  • Puppeteer is unsurprisingly using child_process.spawn() to spawn a new browser
  • nyc is using spawn-wrap for such child processes
  • spawn-wrap is reading the whole executable (./node_modules/puppeteer/.local-chromium/linux-686378/chrome-linux/chrome) into memory with fs.readFileSync which is taking an unusually long time to finish

spawn-wraps README delivers some kind of an explanation:

The initial wrap call uses synchronous I/O. Probably you should not be using this script in any production environments anyway. Also, this will slow down child process execution by a lot, since we're adding a few layers of indirection.

For me personally the answer is that I cannot get the same performance for running tests with and without code coverage as long as I use nyc/istanbul.

I've given c8 a shot, and the performance is nearly the same and I can still have code coverage.