Есть ли способ улучшить скорость передачи данных между Puppeteer и NodeJS?

#node.js #puppeteer

#node.js #puppeteer

Вопрос:

Прежде чем я задам свой вопрос, я почти уверен, что ответ отрицательный, но все же нет ничего постыдного в том, чтобы спросить.

Я пытаюсь использовать THREE Puppeteer в качестве движка рендеринга на стороне сервера. Это означает, что я могу генерировать 3D-изображения в NodeJS, используя WebGL в безголовом браузере. Это работает идеально, за исключением производительности, которую я получаю от этого. И, к моему удивлению, узким местом является не 3D-рендеринг, а передача отрисованного изображения в NodeJS!

Рассмотрим следующий код:

 const puppeteer = require('puppeteer');
const {
    performance,
    PerformanceObserver
} = require('perf_hooks');

const profiling = {};
const obs = new PerformanceObserver((list) => {
    const entry = list.getEntries()[0];
    if (profiling[entry.name]) {
        profiling[entry.name].push(entry.duration);
    } else {
        profiling[entry.name] = [entry.duration];
    }
});
obs.observe({entryTypes: ['measure'], buffered: false});


const pup = puppeteer.launch({
    headless: true,
    args: [
        '--use-gl=swiftshader',
        '--no-sandbox',
        '--enable-surface-synchronization',
    ]
}).then(async browser => {
    const page = (await browser.pages())[0];
    await page.setViewport({width: 1280, height: 720});

    await page.exposeFunction('mark', (name) => {
        performance.mark(name);
    });

    await page.exposeFunction('measure', (name, start, end) => {
        performance.measure(name, start, end);
    });

    //TODO: Load a web page or write a script to instantiate a scene
    // using THREE.js (I've tested with WebGLRenderer)

    for (let i=0; i<100; i  ) {
        window.mark('evaluate-init');
        const txt = await page.evaluate(async () => {
            window.mark('browser_context-init');
            //Render your scene, you can also make a change to the scene just to make sure
            // it will be rendered and you won't just be returning a cache.
            renderer.render(scene, camera);
            const canvas = renderer.domElement;
            const txt = canvas.toDataURL("image/png");
            window.mark('browser_context-end');
            window.measure('browser_context', 'browser_context-init', 'browser_context-end');
            return txt;
        });
        window.mark('evaluate-end');
        window.measure('evaluate', 'evaluate-init', 'evaluate-end');

        performance.mark("node_context-init");
        const data = txt.replace(/^data:image/png;base64,/, "");
        const png = Buffer.from(data, 'base64');
        //TODO: You can do something with the png
        performance.mark("node_context-end");
        performance.measure("node_context", "node_context-init", "node_context-end");
    }

    const profiling_results = {};
    for (let name of Object.keys(profiling)) {
        const sum = profiling[name].reduce((a, b) => a   b, 0);
        const count = profiling[name].length;
        const avg = (sum / count).toFixed(2);
        profiling_results[name] = avg;
    }
    console.log(JSON.stringify(profiling_results, null, 2));
});
  

Основываясь на моих тестах, это средние значения для каждого раздела:

 evaluate: 113.49 ms
browser_context: 0.16 ms
node_context: 5.29 ms
  

С моей точки зрения, 113,49 — 0,16 — это время, затраченное на передачу данных из безголового браузера в память NodeJS. И, как вы можете видеть, это основная часть времени, затраченного на сегодняшний день.

Теперь, после всего этого, мой вопрос таков: могу ли я каким-либо образом улучшить производительность передачи данных между Puppeteer и NodeJS?

Комментарии:

1. Я предполагаю, что есть некоторые накладные расходы на преобразование в строку и обратно … canvas.getImageData() улучшает ли это вообще?

2. Хотя это происходит через протокол devtools (который, я думаю, через websocket), так что в любом случае может оказаться stringified.

3. Я выполнил более детальное профилирование, и большая часть времени, затраченного на самом деле, заключается в передаче данных из контекста браузера в контекст NodeJS. Кроме того, Puppeteer не поддерживает двоичные данные, что означает, что использование base64 неизбежно.

4. Какова ваша конечная цель? Может быть другой способ обойти браузер > мост узлов.

5. Это было бы здорово. Моя конечная цель в некоторой степени объясняется в вопросе. Я пытаюсь отобразить 3D-сцену и записать ее в прямой эфир (поддельная веб-камера). Но прямо сейчас частота кадров слишком низкая, а прямая трансляция составляет 5 кадров в секунду.