Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(syrup): Minimize expensive operations when encoding #2000

Merged
merged 4 commits into from
Jan 30, 2024

Conversation

gibson042
Copy link
Contributor

refs: #1984

Description

encodeString:

  • Avoid redundant encoding.
  • Cache length to avoid re-reading.
  • Anticipate the likely count of prefix bytes to avoid moving data.

encodeArray:

  • Account for element minimum size in encodeArray buffer expansion

encodeAny:

  • Pass diagnostic path data as an array rather than a string
    (In the absence of errors, the associated string concatenation doesn't even take place.)
Effect
esbench -n 100 --repetitions 10 --eshost-option '-h V8,*XS' --eshost-option '-m' \
--init-file <(printf '%s\n' '
    import { smuggle } from "data:text/javascript,delete globalThis.harden; if(typeof TextEncoder === `undefined`) Object.defineProperties(globalThis, { TextDecoder: { writable: true, enumerable: false, configurable: true, value: class TextDecoder {} }, TextEncoder: { writable: true, enumerable: false, configurable: true, value: class TextEncoder { encodeInto(str, buf){ const bufLen = buf.length; let read = 0, written = 0; if (bufLen > 0) for(const ch of str){ const cp = ch.codePointAt(0), n = cp >= 0x10000 ? 4 : cp >= 0x800 ? 3 : cp >= 0x80 ? 2 : 1; if(written + n > bufLen) break; read += ch.length; written += n; if(written === bufLen) break; } return { read, written }; } } }, }); globalThis.console ||= Object.fromEntries(`debug log info warn error groupCollapsed groupEnd`.split(` `).map(m => [m, print])); /* abuse console.clear to smuggle values */ let smuggled; export const smuggle = val => { smuggled = val; }; { globalThis.process ||= { env: {} }; process.env.LOCKDOWN_CONSOLE_TAMING = `unsafe`; console.clear = () => smuggled; }";
    import "@endo/init";
    import { encodeSyrup } from "@endo/syrup";
    smuggle({ encodeSyrup });
  ' | \
  npx rollup@4.9.5 -p @rollup/plugin-node-resolve -p ~/lib/rollup-plugin-js-data-url.js -f iife | \
  npx terser@v5.26.0 -cm --toplevel
) \
'
  const { encodeSyrup } = console.clear(), capacity = 1;
  const str = "A".repeat(42), short = Array(5).fill(str), mid = Array(100).fill(str), long = Array(1000).fill(str);
' '{
  encodeShort: `result = encodeSyrup(short, { length: capacity });`,
  encodeMid: `result = encodeSyrup(mid, { length: capacity });`,
  encodeLong: `result = encodeSyrup(long, { length: capacity });`,
}'

Before:

#### Moddable XS
encodeShort: 2.08 ops/ms
encodeMid: 0.22 ops/ms
encodeLong: 0.02 ops/ms

#### V8
encodeShort: 8.33 ops/ms
encodeMid: 1.14 ops/ms
encodeLong: 0.12 ops/ms

After:

#### Moddable XS
encodeShort: 3.03 ops/ms
encodeMid: 0.23 ops/ms
encodeLong: 0.02 ops/ms

#### V8
encodeShort: 10.00 ops/ms
encodeMid: 1.35 ops/ms
encodeLong: 0.16 ops/ms

Security Considerations

None known.

Scaling Considerations

For large strings, performance is still dominated by currently-unavoidable O(n) operations.

Documentation Considerations

n/a

Testing Considerations

n/a

Upgrade Considerations

n/a

@kriskowal
Copy link
Member

(I don’t advise spending a great deal of time on @endo/syrup. I suspect that the next run at Syrup, if one should occur, is to turn it into a streaming codec to avoid allocating an intermediate representation.)

@gibson042 gibson042 enabled auto-merge January 30, 2024 21:51
* Avoid redundant encoding.
* Cache `length` to avoid re-reading.
* Anticipate the likely count of prefix bytes to avoid moving data.
In the absence of errors, the associated string concatenation doesn't even take place.
@gibson042 gibson042 force-pushed the gibson-2024-01-encodesyrup-perf branch from da2641b to 5ef0882 Compare January 30, 2024 21:51
@gibson042 gibson042 merged commit ba49526 into master Jan 30, 2024
14 checks passed
@gibson042 gibson042 deleted the gibson-2024-01-encodesyrup-perf branch January 30, 2024 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants