[skwasm] Use queueMicrotask instead of postMessage when single-threaded (#166997)

It turns out `postMessage` is quite a bit more expensive than
`queueMicrotask`. Before dynamic threading, this is what we actually
did, and perf regressed when we started using `postMessage` instead.

This fixes https://github.com/flutter/flutter/issues/166905
This commit is contained in:
Jackson Gardner 2025-04-14 10:59:10 -07:00 committed by GitHub
parent 0aca715089
commit 2f6cdc3fe1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -8,51 +8,72 @@
mergeInto(LibraryManager.library, {
$skwasm_support_setup__postset: 'skwasm_support_setup();',
$skwasm_support_setup: function() {
// This value represents the difference between the time origin of the main
// thread and whichever web worker this code is running on. This is so that
// when we report frame timings, that they are in the same time domain
// regardless of whether they are captured on the main thread or the web
// worker.
let timeOriginDelta = 0;
skwasm_registerMessageListener = function(threadId, listener) {
const eventListener = function({data}) {
const skwasmMessage = data.skwasmMessage;
if (!skwasmMessage) {
return;
}
if (skwasmMessage == 'syncTimeOrigin') {
timeOriginDelta = performance.timeOrigin - data.timeOrigin;
return;
}
listener(data);
if (Module["skwasmSingleThreaded"]) {
_skwasm_isSingleThreaded = function() {
return true;
};
if (!threadId) {
addEventListener("message", eventListener);
} else {
_wasmWorkers[threadId].addEventListener("message", eventListener);
_wasmWorkers[threadId].postMessage({
skwasmMessage: 'syncTimeOrigin',
timeOrigin: performance.timeOrigin,
});
let messageListener;
// In single threaded mode, we simply invoke the message listener as a
// microtask, as it's much cheaper than doing a full postMessage
skwasm_registerMessageListener = function(threadId, listener) {
messageListener = listener;
}
};
skwasm_getCurrentTimestamp = function() {
return performance.now() + timeOriginDelta;
};
skwasm_postMessage = function(message, transfers, threadId) {
if (threadId) {
_wasmWorkers[threadId].postMessage(message, transfers);
} else {
postMessage(message, transfers);
}
};
skwasm_getCurrentTimestamp = function() {
return performance.now();
};
skwasm_postMessage = function(message, transfers, threadId) {
// If we're in single-threaded mode, we shouldn't use postMessage, as
// it ends up being quite expensive. Instead, just queue a microtask.
queueMicrotask(() => messageListener(message));
};
} else {
_skwasm_isSingleThreaded = function() {
return false;
};
// This value represents the difference between the time origin of the main
// thread and whichever web worker this code is running on. This is so that
// when we report frame timings, that they are in the same time domain
// regardless of whether they are captured on the main thread or the web
// worker.
let timeOriginDelta = 0;
skwasm_registerMessageListener = function(threadId, listener) {
const eventListener = function({data}) {
const skwasmMessage = data.skwasmMessage;
if (!skwasmMessage) {
return;
}
if (skwasmMessage == 'syncTimeOrigin') {
timeOriginDelta = performance.timeOrigin - data.timeOrigin;
return;
}
listener(data);
};
if (!threadId) {
addEventListener("message", eventListener);
} else {
_wasmWorkers[threadId].addEventListener("message", eventListener);
_wasmWorkers[threadId].postMessage({
skwasmMessage: 'syncTimeOrigin',
timeOrigin: performance.timeOrigin,
});
}
};
skwasm_getCurrentTimestamp = function() {
return performance.now() + timeOriginDelta;
};
skwasm_postMessage = function(message, transfers, threadId) {
if (threadId) {
_wasmWorkers[threadId].postMessage(message, { transfer: transfers } );
} else {
postMessage(message, { transfer: transfers });
}
};
}
const handleToCanvasMap = new Map();
const associatedObjectsMap = new Map();
_skwasm_isSingleThreaded = function() {
return Module["skwasmSingleThreaded"];
};
_skwasm_setAssociatedObjectOnThread = function(threadId, pointer, object) {
skwasm_postMessage({
skwasmMessage: 'setAssociatedObject',