更快的网络工作者消息传递

时间:2020-04-16 01:42:16

标签: javascript performance web-worker

我已经在Chrome中专门测试了网络工作者消息传递,并获得了大约50ms的延迟来发送和接收消息:

// Sender section

let imageData = mCtx.getImageData(0, 0, w, h);
let bitmapData = await createImageBitmap(imageData);
beforeAddBitmapFrame = performance.now();
videoWorker.postMessage({ action : 'addFrameBitmap', data: bitmapData }, [bitmapData]);


// Receiver section

videoWorker.onmessage = function (e) {
    let blob = e.data.data;
    beforeRenderBlobFrame = performance.now();
    let latency = (beforeRenderBlobFrame - beforeAddBitmapFrame); // 50ms
    if(latency > 10) {
       console.log('=== Max Latency Hit ===')
    }
    renderBlobTest(blob);
};

这基本上是一个循环测试,其中将图像发送给Web Worker,Web Worker会将其发送回去以计算延迟。乍一看,这里的50毫秒可能什么都不是,但是如果像30 FPS的视频那样乘以它,那么算一下,50 ms x 30 frames = 1500 ms延迟(1.5秒)就算不上网络传输了很多。

如何减少Web Worker消息传递的延迟?

[UPDATE] 为了进一步测试,我以给定的时间间隔对网络工作者进行了简单的“ ping”测试

setInterval(function () {
  let pingTime = new Date().getMilliseconds();
  videoWorker.postMessage({ action: 'ping', pingTime : pingTime });
}, 500);

然后做了

if(e.data.pingTime) {
  let pongTime = new Date().getMilliseconds();
  console.log('Got pong: ' + ( pongTime - e.data.pingTime ))
}

高于它的类似结果平均为〜50ms。

1 个答案:

答案 0 :(得分:0)

您感觉到了一个微基准测试陷阱:

永远不要运行测试的单个实例。

第一次运行总是会变慢,引擎必须预热,在您的情况下,必须生成整个Worker线程,并且必须初始化许多其他内容(有关列表,请参见this Q/A)导致第一个消息延迟的原因。 此外,由于某些外部事件和不相关事件,单个测试很容易报告完全错误的结果(后台应用程序决定此时执行一些操作,垃圾收集器启动,UI事件等等)。

const videoWorker = new Worker( generateWorkerURL() );

let startTime;
const latencies = [];
const max_rounds = 10;

// Receiver section
videoWorker.onmessage = function (e) {
  const endTime = performance.now();
  e.data.close();
  const latency = (endTime - startTime);
  // store the current latency
  latencies.push( latency );
  if( latencies.length < max_rounds ) {
    performTest();
  }
  else {
    logResults();
  }
};
// initial call
performTest();

// the actual test code
async function performTest() {
  // we'll build a new random image every test
  const w = 1920;
  const h = 1080;
  // make some noise
  const data = Uint32Array.from( { length: w * h }, ()=> Math.random * 0xFFFFFF + 0xFF000000);
  const imageData = new ImageData( new Uint8ClampedArray( data.buffer ), w, h );
  let bitmapData = await createImageBitmap(imageData);
  // start measuring the time it takes to transfer
  startTime = performance.now();
  videoWorker.postMessage( bitmapData, [ bitmapData ] );
}

// when all the tests are done
function logResults() {
  const total = latencies.reduce( (total, lat) => total + lat );
  const avg = total / latencies.length;
  console.log( "average latency (ms)", avg );
  console.log( "first ten absolute values", latencies.slice( 0, 10 ) );
}

function generateWorkerURL() {
  const content = `onmessage = e => postMessage( e.data, [e.data] );`;
  const blob = new Blob( [ content ], { type: 'text/javacript' } );
  return URL.createObjectURL( blob );
}

运行1000个测试平均导致我的计算机上每个测试平均<1.2毫秒(如果每个测试(即不使用GC)不生成新的ImageData,则平均为0.12毫秒),而第一次运行大约需要11毫秒。
这些结果表明,数据传输几乎不需要时间(几乎与等待下一个事件循环一样快)。

所以您的瓶颈在另一个城堡,并且在消息传递部分没有任何要加快的事情。
请记住,如果您的主线程被阻止,从该主线程触发的处理程序也将被阻止。