1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

perf(ext/web): optimize structuredClone without transferables (#20730)

This PR optimizes `structuredClone` when it's called without
transferables.

### Benchmarks

**main**
```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.37.1 (x86_64-unknown-linux-gnu)

benchmark                          time (avg)        iter/s             (min … max)       p75       p99      p995
----------------------------------------------------------------------------------- -----------------------------
structuredClone object              1.64 µs/iter     611,086.0     (1.58 µs … 1.84 µs)   1.66 µs   1.84 µs   1.84 µs
structuredClone transferables       2.82 µs/iter     354,281.4     (2.78 µs … 2.92 µs)   2.84 µs   2.92 µs   2.92 µs
```

**this PR**

```
cpu: 13th Gen Intel(R) Core(TM) i9-13900H
runtime: deno 1.37.1 (x86_64-unknown-linux-gnu)

structuredClone object                 1 µs/iter     998,383.5    (971.28 ns … 1.2 µs)      1 µs    1.2 µs    1.2 µs
structuredClone transferables       2.82 µs/iter     355,087.5      (2.7 µs … 3.07 µs)   2.83 µs   3.07 µs   3.07 µs
```

```js
Deno.bench("structuredClone object", () => {
  structuredClone({ foo: "bar" });
});

Deno.bench("structuredClone transferables", () => {
  const buf = new Uint8Array([97]);
  structuredClone(buf, {
    transfer: [buf.buffer],
  });
});
```
This commit is contained in:
Marcos Casagrande 2023-10-06 23:21:48 +02:00 committed by GitHub
parent cba5ae45c2
commit ceecd8c495
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -205,34 +205,39 @@ function opCreateEntangledMessagePort() {
function deserializeJsMessageData(messageData) { function deserializeJsMessageData(messageData) {
/** @type {object[]} */ /** @type {object[]} */
const transferables = []; const transferables = [];
const hostObjects = [];
const arrayBufferIdsInTransferables = []; const arrayBufferIdsInTransferables = [];
const transferredArrayBuffers = []; const transferredArrayBuffers = [];
let options;
for (let i = 0; i < messageData.transferables.length; ++i) { if (messageData.transferables.length > 0) {
const transferable = messageData.transferables[i]; const hostObjects = [];
switch (transferable.kind) { for (let i = 0; i < messageData.transferables.length; ++i) {
case "messagePort": { const transferable = messageData.transferables[i];
const port = createMessagePort(transferable.data); switch (transferable.kind) {
ArrayPrototypePush(transferables, port); case "messagePort": {
ArrayPrototypePush(hostObjects, port); const port = createMessagePort(transferable.data);
break; ArrayPrototypePush(transferables, port);
ArrayPrototypePush(hostObjects, port);
break;
}
case "arrayBuffer": {
ArrayPrototypePush(transferredArrayBuffers, transferable.data);
const index = ArrayPrototypePush(transferables, null);
ArrayPrototypePush(arrayBufferIdsInTransferables, index);
break;
}
default:
throw new TypeError("Unreachable");
} }
case "arrayBuffer": {
ArrayPrototypePush(transferredArrayBuffers, transferable.data);
const index = ArrayPrototypePush(transferables, null);
ArrayPrototypePush(arrayBufferIdsInTransferables, index);
break;
}
default:
throw new TypeError("Unreachable");
} }
options = {
hostObjects,
transferredArrayBuffers,
};
} }
const data = core.deserialize(messageData.data, { const data = core.deserialize(messageData.data, options);
hostObjects,
transferredArrayBuffers,
});
for (let i = 0; i < arrayBufferIdsInTransferables.length; ++i) { for (let i = 0; i < arrayBufferIdsInTransferables.length; ++i) {
const id = arrayBufferIdsInTransferables[i]; const id = arrayBufferIdsInTransferables[i];
@ -248,31 +253,36 @@ function deserializeJsMessageData(messageData) {
* @returns {messagePort.MessageData} * @returns {messagePort.MessageData}
*/ */
function serializeJsMessageData(data, transferables) { function serializeJsMessageData(data, transferables) {
let options;
const transferredArrayBuffers = []; const transferredArrayBuffers = [];
for (let i = 0, j = 0; i < transferables.length; i++) { if (transferables.length > 0) {
const ab = transferables[i]; const hostObjects = [];
if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, ab)) { for (let i = 0, j = 0; i < transferables.length; i++) {
if ( const t = transferables[i];
ArrayBufferPrototypeGetByteLength(ab) === 0 && if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, t)) {
ops.op_arraybuffer_was_detached(ab) if (
) { ArrayBufferPrototypeGetByteLength(t) === 0 &&
throw new DOMException( ops.op_arraybuffer_was_detached(t)
`ArrayBuffer at index ${j} is already detached`, ) {
"DataCloneError", throw new DOMException(
); `ArrayBuffer at index ${j} is already detached`,
"DataCloneError",
);
}
j++;
ArrayPrototypePush(transferredArrayBuffers, t);
} else if (ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)) {
ArrayPrototypePush(hostObjects, t);
} }
j++;
ArrayPrototypePush(transferredArrayBuffers, ab);
} }
options = {
hostObjects,
transferredArrayBuffers,
};
} }
const serializedData = core.serialize(data, { const serializedData = core.serialize(data, options, (err) => {
hostObjects: ArrayPrototypeFilter(
transferables,
(a) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, a),
),
transferredArrayBuffers,
}, (err) => {
throw new DOMException(err, "DataCloneError"); throw new DOMException(err, "DataCloneError");
}); });