perf(ext/websocket): efficient event kind serialization (#18509)

Use u16 to represent the kind of event (0 - 6) & event code > 6 is
treated as the close code. This way we can represent all events + the
close code in a single JS number. This is safe because (as per RFC 6455)
close code from 0-999 are reserved & not used.

| name | avg msg/sec/core |
| --- | --- |
| deno_main | `127820.750000` |
| deno #18506 | `140079.000000` |
| deno #18506 + this | `150104.250000` |
This commit is contained in:
Divy Srivastava 2023-03-31 10:34:12 +05:30 committed by GitHub
parent 7722014497
commit 0f41aff1d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 73 deletions

View file

@ -394,13 +394,14 @@ class WebSocket extends EventTarget {
async [_eventLoop]() {
while (this[_readyState] !== CLOSED) {
const { kind, value } = await core.opAsync(
const { 0: kind, 1: value } = await core.opAsync(
"op_ws_next_event",
this[_rid],
);
switch (kind) {
case "string": {
case 0: {
/* string */
this[_serverHandleIdleTimeout]();
const event = new MessageEvent("message", {
data: value,
@ -409,14 +410,15 @@ class WebSocket extends EventTarget {
this.dispatchEvent(event);
break;
}
case "binary": {
case 1: {
/* binary */
this[_serverHandleIdleTimeout]();
let data;
if (this.binaryType === "blob") {
data = new Blob([value]);
} else {
data = value.buffer;
data = value;
}
const event = new MessageEvent("message", {
@ -427,39 +429,13 @@ class WebSocket extends EventTarget {
this.dispatchEvent(event);
break;
}
case "pong": {
case 2: {
/* pong */
this[_serverHandleIdleTimeout]();
break;
}
case "closed":
case "close": {
const prevState = this[_readyState];
this[_readyState] = CLOSED;
clearTimeout(this[_idleTimeoutTimeout]);
if (prevState === OPEN) {
try {
await core.opAsync(
"op_ws_close",
this[_rid],
value.code,
value.reason,
);
} catch {
// ignore failures
}
}
const event = new CloseEvent("close", {
wasClean: true,
code: value.code,
reason: value.reason,
});
this.dispatchEvent(event);
core.tryClose(this[_rid]);
break;
}
case "error": {
case 5: {
/* error */
this[_readyState] = CLOSED;
const errorEv = new ErrorEvent("error", {
@ -472,6 +448,39 @@ class WebSocket extends EventTarget {
core.tryClose(this[_rid]);
break;
}
case 3: {
/* ping */
break;
}
default: {
/* close */
const code = kind;
const prevState = this[_readyState];
this[_readyState] = CLOSED;
clearTimeout(this[_idleTimeoutTimeout]);
if (prevState === OPEN) {
try {
await core.opAsync(
"op_ws_close",
this[_rid],
code,
value,
);
} catch {
// ignore failures
}
}
const event = new CloseEvent("close", {
wasClean: true,
code: code,
reason: value,
});
this.dispatchEvent(event);
core.tryClose(this[_rid]);
break;
}
}
}
}