perf: resolve `Promise`s natively for async ops
Supersede #1158
Waiting for
- [x] ~~https://github.com/denoland/rusty_v8/issues/1818~~
- [ ] waiting for for deno_core updates to the latest rusty_v8
Summary
22% more performant async ops,
(by resolve Promises natively)
and less memory usage (no need of preparing an array of results)
const ops = Deno.core.ops;
// ops.op_print(Object.keys(ops).join('\n')+'\n')
function tImm(tag, p) {
const symbol = Symbol('k.notImm')
function check(v) {
const imm = v !== symbol
// ops.op_print(`@${tag} ${imm}\n`)
}
const p1 = Promise.any([p, Promise.resolve(symbol)])
p1.then(check, check)
}
for (let i = 0; i < 50000; i++) {
tImm("op_void_async", ops.op_void_async())
tImm("op_void_async_deferred", ops.op_void_async_deferred())
tImm("op_error_async", ops.op_error_async())
}
// ops.op_print("Hello World\n")
Help wanted
async stacktrace for error is gone rn
I need do the same but in native:
somewhat solution: (obviously bad, but at least it kinda works)
Don't have a bindings for this, also this is private i guess (https://github.com/v8/v8/blob/e3529d092163dcfbbb454257dc4103bdebfeda48/src/execution/messages.h#L150
This one should be public?, but still no binding for it i guess: https://github.com/v8/v8/blob/e3529d092163dcfbbb454257dc4103bdebfeda48/include/v8-exception.h#L69
I didn't found a considerable solutions, maybe I need to add bindings to rusty_v8 first? UPDATE: https://github.com/denoland/rusty_v8/issues/1818
@CyanChanges can you please provide result when running this code in your PR and on main?
// bench.js
const ops = Deno.core.ops;
// ops.op_print(Object.keys(ops).join('\n')+'\n')
async function bench(name, op) {
const start = new Date();
for (let i = 0; i < 500000; i++) {
await op();
}
const end = new Date();
const duration = end - start;
ops.op_print(`${name} took ${duration}ms\n`);
}
await bench("op_void_async", ops.op_void_async)
await bench("op_void_async_deferred", ops.op_void_async_deferred)
target/release/dcore bench.js
// bench_batched.js
const ops = Deno.core.ops;
// ops.op_print(Object.keys(ops).join('\n')+'\n')
async function bench(name, op) {
const start = new Date();
const batched = new Array(500000).fill(op);
await Promise.all(batched.map((fn) => fn()));
const end = new Date();
const duration = end - start;
ops.op_print(`${name} took ${duration}ms\n`);
}
await bench("op_void_async", ops.op_void_async)
await bench("op_void_async_deferred", ops.op_void_async_deferred)
target/release/dcore bench_batched.js
From our benchmarks this PR appears to be significantly slower than main:
// this PR
target/release/dcore bench.js
🛑 deno_core binary is meant for development and testing purposes.
Run bench.js
op_void_async took 198ms
op_void_async_deferred took 1166ms
// main
./dcore_main bench.js [INS]
🛑 deno_core binary is meant for development and testing purposes.
Run bench.js
op_void_async took 35ms
op_void_async_deferred took 974ms
@CyanChanges can you please provide result when running this code in your PR and on
main?// bench.js const ops = Deno.core.ops; // ops.op_print(Object.keys(ops).join('\n')+'\n') async function bench(name, op) { const start = new Date(); for (let i = 0; i < 500000; i++) { await op(); } const end = new Date(); const duration = end - start; ops.op_print(`${name} took ${duration}ms\n`); } await bench("op_void_async", ops.op_void_async) await bench("op_void_async_deferred", ops.op_void_async_deferred)target/release/dcore bench.js// bench_batched.js const ops = Deno.core.ops; // ops.op_print(Object.keys(ops).join('\n')+'\n') async function bench(name, op) { const start = new Date(); const batched = new Array(500000).fill(op); await Promise.all(batched.map((fn) => fn())); const end = new Date(); const duration = end - start; ops.op_print(`${name} took ${duration}ms\n`); } await bench("op_void_async", ops.op_void_async) await bench("op_void_async_deferred", ops.op_void_async_deferred)target/release/dcore bench_batched.jsFrom our benchmarks this PR appears to be significantly slower than
main:// this PR target/release/dcore bench.js 🛑 deno_core binary is meant for development and testing purposes. Run bench.js op_void_async took 198ms op_void_async_deferred took 1166ms// main ./dcore_main bench.js [INS] 🛑 deno_core binary is meant for development and testing purposes. Run bench.js op_void_async took 35ms op_void_async_deferred took 974ms
@CyanChanges can you please provide result when running this code in your PR and on
main?// bench.js const ops = Deno.core.ops; // ops.op_print(Object.keys(ops).join('\n')+'\n') async function bench(name, op) { const start = new Date(); for (let i = 0; i < 500000; i++) { await op(); } const end = new Date(); const duration = end - start; ops.op_print(`${name} took ${duration}ms\n`); } await bench("op_void_async", ops.op_void_async) await bench("op_void_async_deferred", ops.op_void_async_deferred)target/release/dcore bench.js// bench_batched.js const ops = Deno.core.ops; // ops.op_print(Object.keys(ops).join('\n')+'\n') async function bench(name, op) { const start = new Date(); const batched = new Array(500000).fill(op); await Promise.all(batched.map((fn) => fn())); const end = new Date(); const duration = end - start; ops.op_print(`${name} took ${duration}ms\n`); } await bench("op_void_async", ops.op_void_async) await bench("op_void_async_deferred", ops.op_void_async_deferred)target/release/dcore bench_batched.jsFrom our benchmarks this PR appears to be significantly slower than
main:// this PR target/release/dcore bench.js 🛑 deno_core binary is meant for development and testing purposes. Run bench.js op_void_async took 198ms op_void_async_deferred took 1166ms// main ./dcore_main bench.js [INS] 🛑 deno_core binary is meant for development and testing purposes. Run bench.js op_void_async took 35ms op_void_async_deferred took 974ms
I just switched to NixOS recently, so I needs to configure the dev environment and rebuild it. It may take a while tho
@CyanChanges can you please provide result when running this code in your PR and on
main?// bench.js const ops = Deno.core.ops; // ops.op_print(Object.keys(ops).join('\n')+'\n') async function bench(name, op) { const start = new Date(); for (let i = 0; i < 500000; i++) { await op(); } const end = new Date(); const duration = end - start; ops.op_print(`${name} took ${duration}ms\n`); } await bench("op_void_async", ops.op_void_async) await bench("op_void_async_deferred", ops.op_void_async_deferred)target/release/dcore bench.js// bench_batched.js const ops = Deno.core.ops; // ops.op_print(Object.keys(ops).join('\n')+'\n') async function bench(name, op) { const start = new Date(); const batched = new Array(500000).fill(op); await Promise.all(batched.map((fn) => fn())); const end = new Date(); const duration = end - start; ops.op_print(`${name} took ${duration}ms\n`); } await bench("op_void_async", ops.op_void_async) await bench("op_void_async_deferred", ops.op_void_async_deferred)target/release/dcore bench_batched.jsFrom our benchmarks this PR appears to be significantly slower than
main:// this PR target/release/dcore bench.js 🛑 deno_core binary is meant for development and testing purposes. Run bench.js op_void_async took 198ms op_void_async_deferred took 1166ms// main ./dcore_main bench.js [INS] 🛑 deno_core binary is meant for development and testing purposes. Run bench.js op_void_async took 35ms op_void_async_deferred took 974ms