From 252a3e35fb77de47e6e7202a63a54129556ceb84 Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Fri, 30 Aug 2019 11:59:04 +0200 Subject: [PATCH] Reduce the amount of unnecessary function calls and object allocations, in `MessageHandler`, when using Streams With PR 11069 we're now using Streams for OperatorList parsing (in addition to just TextContent parsing), which brings the nice benefit of being able to easily abort parsing on the worker-thread thus saving resources. However, since we're now creating many more `ReadableStream` there appears to be a tiny bit more overhead because of it (giving ~1% slower runtime of `browsertest` on the bots). In this case we're just going to have to accept such a small regression, since the benefits of using Streams clearly outweighs it. What we *can* do here, is to try and make the Streams part of the `MessageHandler` implementation slightly more efficient by e.g. removing unnecessary function calls (which has been helpful in other parts of the code-base). To that end, this patch makes the following changes: - Actually support `transfers` in `MessageHandler.sendWithStream`, since the parameter was being ignored. - Inline the `sendStreamRequest`/`sendStreamResponse` helper functions at their respective call-sites. Obviously this causes some amount of code duplication, however I still think this change seems reasonable since for each call-site: - It avoids making one unnecessary function call. - It avoids allocating one temporary object. - It avoids sending, and thus structure clone, various undefined object properties. - Inline objects in the `MessageHandler.{send, sendWithPromise}` methods. - Finally, directly call `comObj.postMessage` in various methods when `transfers` are *not* present, rather than calling `MessageHandler.postMessage`, to further reduce the amount of function calls. --- src/shared/message_handler.js | 127 +++++++++++++++++++++++----------- 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/src/shared/message_handler.js b/src/shared/message_handler.js index 29e16b718..961c19be2 100644 --- a/src/shared/message_handler.js +++ b/src/shared/message_handler.js @@ -143,13 +143,12 @@ MessageHandler.prototype = { * @param {Array} [transfers] - Optional list of transfers/ArrayBuffers */ send(actionName, data, transfers) { - var message = { + this.postMessage({ sourceName: this.sourceName, targetName: this.targetName, action: actionName, data, - }; - this.postMessage(message, transfers); + }, transfers); }, /** * Sends a message to the comObj to invoke the action with the supplied data. @@ -161,19 +160,18 @@ MessageHandler.prototype = { */ sendWithPromise(actionName, data, transfers) { var callbackId = this.callbackId++; - var message = { - sourceName: this.sourceName, - targetName: this.targetName, - action: actionName, - data, - callbackId, - }; var capability = createPromiseCapability(); this.callbacksCapabilities[callbackId] = capability; try { - this.postMessage(message, transfers); - } catch (e) { - capability.reject(e); + this.postMessage({ + sourceName: this.sourceName, + targetName: this.targetName, + action: actionName, + callbackId, + data, + }, transfers); + } catch (ex) { + capability.reject(ex); } return capability.promise; }, @@ -191,6 +189,7 @@ MessageHandler.prototype = { let streamId = this.streamId++; let sourceName = this.sourceName; let targetName = this.targetName; + const comObj = this.comObj; return new ReadableStream({ start: (controller) => { @@ -207,7 +206,7 @@ MessageHandler.prototype = { streamId, data, desiredSize: controller.desiredSize, - }); + }, transfers); // Return Promise for Async process, to signal success/failure. return startCapability.promise; }, @@ -215,7 +214,7 @@ MessageHandler.prototype = { pull: (controller) => { let pullCapability = createPromiseCapability(); this.streamControllers[streamId].pullCall = pullCapability; - this.postMessage({ + comObj.postMessage({ sourceName, targetName, stream: 'pull', @@ -231,12 +230,12 @@ MessageHandler.prototype = { let cancelCapability = createPromiseCapability(); this.streamControllers[streamId].cancelCall = cancelCapability; this.streamControllers[streamId].isClosed = true; - this.postMessage({ + comObj.postMessage({ sourceName, targetName, stream: 'cancel', - reason, streamId, + reason, }); // Return Promise to signal success or failure. return cancelCapability.promise; @@ -252,12 +251,7 @@ MessageHandler.prototype = { let sourceName = this.sourceName; let targetName = data.sourceName; let capability = createPromiseCapability(); - - let sendStreamRequest = ({ stream, chunk, transfers, - success, reason, }) => { - this.postMessage({ sourceName, targetName, stream, streamId, - chunk, success, reason, }, transfers); - }; + const comObj = this.comObj; let streamSink = { enqueue(chunk, size = 1, transfers) { @@ -273,7 +267,13 @@ MessageHandler.prototype = { this.sinkCapability = createPromiseCapability(); this.ready = this.sinkCapability.promise; } - sendStreamRequest({ stream: 'enqueue', chunk, transfers, }); + self.postMessage({ + sourceName, + targetName, + stream: 'enqueue', + streamId, + chunk, + }, transfers); }, close() { @@ -281,7 +281,12 @@ MessageHandler.prototype = { return; } this.isCancelled = true; - sendStreamRequest({ stream: 'close', }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'close', + streamId, + }); delete self.streamSinks[streamId]; }, @@ -290,7 +295,13 @@ MessageHandler.prototype = { return; } this.isCancelled = true; - sendStreamRequest({ stream: 'error', reason, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'error', + streamId, + reason, + }); }, sinkCapability: capability, @@ -305,9 +316,21 @@ MessageHandler.prototype = { streamSink.ready = streamSink.sinkCapability.promise; this.streamSinks[streamId] = streamSink; resolveCall(action[0], [data.data, streamSink], action[1]).then(() => { - sendStreamRequest({ stream: 'start_complete', success: true, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'start_complete', + streamId, + success: true, + }); }, (reason) => { - sendStreamRequest({ stream: 'start_complete', success: false, reason, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'start_complete', + streamId, + reason, + }); }); }, @@ -315,11 +338,7 @@ MessageHandler.prototype = { let sourceName = this.sourceName; let targetName = data.sourceName; let streamId = data.streamId; - - let sendStreamResponse = ({ stream, success, reason, }) => { - this.comObj.postMessage({ sourceName, targetName, stream, - success, streamId, reason, }); - }; + const comObj = this.comObj; let deleteStreamController = () => { // Delete the `streamController` only when the start, pull, and cancel @@ -345,7 +364,13 @@ MessageHandler.prototype = { case 'pull': // Ignore any pull after close is called. if (!this.streamSinks[data.streamId]) { - sendStreamResponse({ stream: 'pull_complete', success: true, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'pull_complete', + streamId, + success: true, + }); break; } // Pull increases the desiredSize property of sink, @@ -358,10 +383,21 @@ MessageHandler.prototype = { // Reset desiredSize property of sink on every pull. this.streamSinks[data.streamId].desiredSize = data.desiredSize; resolveCall(this.streamSinks[data.streamId].onPull).then(() => { - sendStreamResponse({ stream: 'pull_complete', success: true, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'pull_complete', + streamId, + success: true, + }); }, (reason) => { - sendStreamResponse({ stream: 'pull_complete', - success: false, reason, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'pull_complete', + streamId, + reason, + }); }); break; case 'enqueue': @@ -398,10 +434,21 @@ MessageHandler.prototype = { } resolveCall(this.streamSinks[data.streamId].onCancel, [wrapReason(data.reason)]).then(() => { - sendStreamResponse({ stream: 'cancel_complete', success: true, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'cancel_complete', + streamId, + success: true, + }); }, (reason) => { - sendStreamResponse({ stream: 'cancel_complete', - success: false, reason, }); + comObj.postMessage({ + sourceName, + targetName, + stream: 'cancel_complete', + streamId, + reason, + }); }); this.streamSinks[data.streamId].sinkCapability. reject(wrapReason(data.reason));