Skip to content

Commit

Permalink
perf(ops): Monomorphic sync op calls (denoland#15337)
Browse files Browse the repository at this point in the history
Welcome to better optimised op calls! Currently opSync is called with parameters of every type and count. This most definitely makes the call megamorphic. Additionally, it seems that spread params leads to V8 not being able to optimise the calls quite as well (apparently Fast Calls cannot be used with spread params).

Monomorphising op calls should lead to some improved performance. Now that unwrapping of sync ops results is done on Rust side, this is pretty simple:

```
opSync("op_foo", param1, param2);
// -> turns to
ops.op_foo(param1, param2);
```

This means sync op calls are now just directly calling the native binding function. When V8 Fast API Calls are enabled, this will enable those to be called on the optimised path.

Monomorphising async ops likely requires using callbacks and is left as an exercise to the reader.
  • Loading branch information
aapoalas committed Aug 11, 2022
1 parent 883269f commit 2164f6b
Show file tree
Hide file tree
Showing 80 changed files with 552 additions and 616 deletions.
2 changes: 1 addition & 1 deletion bench_util/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn setup() -> Vec<Extension> {
}

fn bench_op_nop(b: &mut Bencher) {
bench_js_sync(b, r#"Deno.core.opSync("op_nop", null, null);"#, setup);
bench_js_sync(b, r#"Deno.core.ops.op_nop();"#, setup);
}

benchmark_group!(benches, bench_op_nop);
Expand Down
6 changes: 3 additions & 3 deletions bench_util/benches/op_baseline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ async fn op_pi_async() -> i64 {
}

fn bench_op_pi_json(b: &mut Bencher) {
bench_js_sync(b, r#"Deno.core.opSync("op_pi_json", null);"#, setup);
bench_js_sync(b, r#"Deno.core.ops.op_pi_json();"#, setup);
}

fn bench_op_nop(b: &mut Bencher) {
bench_js_sync(b, r#"Deno.core.opSync("op_nop", null, null, null);"#, setup);
bench_js_sync(b, r#"Deno.core.ops.op_nop();"#, setup);
}

fn bench_op_async(b: &mut Bencher) {
bench_js_async(b, r#"Deno.core.opAsync("op_pi_async", null);"#, setup);
bench_js_async(b, r#"Deno.core.opAsync("op_pi_async");"#, setup);
}

fn bench_is_proxy(b: &mut Bencher) {
Expand Down
6 changes: 5 additions & 1 deletion cli/bench/deno_common.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ Deno.bench("date_now", { n: 5e5 }, () => {
});

// Void ops measure op-overhead
Deno.bench("op_void_sync", { n: 1e7 }, () => Deno.core.opSync("op_void_sync"));
Deno.bench(
"op_void_sync",
{ n: 1e7 },
() => Deno.core.ops.op_void_sync(),
);

Deno.bench(
"op_void_async",
Expand Down
2 changes: 1 addition & 1 deletion cli/bench/http/deno_http_ops.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Http {
}

for await (const conn of tcp) {
const id = Deno.core.opSync("op_http_start", conn.rid);
const id = Deno.core.ops.op_http_start(conn.rid);
const http = new Http(id);
(async () => {
for await (const req of http) {
Expand Down
6 changes: 2 additions & 4 deletions cli/tests/testdata/bench/recursive_permissions_pledge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
Deno.core.opSync(
"op_pledge_test_permissions",
Deno.core.ops.op_pledge_test_permissions(
"none",
);
Deno.core.opSync(
"op_pledge_test_permissions",
Deno.core.ops.op_pledge_test_permissions(
"inherit",
);
2 changes: 1 addition & 1 deletion cli/tests/testdata/op_exit_op_set_exit_code_in_worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// `self.close()`.

// @ts-ignore Deno.core doesn't have type-defs
Deno.core.opSync("op_set_exit_code", 21);
Deno.core.ops.op_set_exit_code(21);

const worker = new Worker(
import.meta.resolve("./op_exit_op_set_exit_code_worker.js"),
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/testdata/op_exit_op_set_exit_code_worker.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
self.onmessage = () => {
Deno.core.opSync("op_set_exit_code", 42);
Deno.core.ops.op_set_exit_code(42);
Deno.exit();
};
2 changes: 1 addition & 1 deletion cli/tests/testdata/set_exit_code_0.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Deno.core.opSync("op_set_exit_code", 42);
Deno.core.ops.op_set_exit_code(42);
Deno.exit(0); // Takes precedence.
2 changes: 1 addition & 1 deletion cli/tests/testdata/set_exit_code_1.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Deno.core.opSync("op_set_exit_code", 42);
Deno.core.ops.op_set_exit_code(42);
Deno.exit();
2 changes: 1 addition & 1 deletion cli/tests/testdata/set_exit_code_2.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
Deno.core.opSync("op_set_exit_code", 42);
Deno.core.ops.op_set_exit_code(42);
// Exits naturally.
6 changes: 2 additions & 4 deletions cli/tests/testdata/test/recursive_permissions_pledge.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
Deno.core.opSync(
"op_pledge_test_permissions",
Deno.core.ops.op_pledge_test_permissions(
"none",
);
Deno.core.opSync(
"op_pledge_test_permissions",
Deno.core.ops.op_pledge_test_permissions(
"inherit",
);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_1.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_load", { path: "", symbols: {} });
Deno.core.ops.op_ffi_load({ path: "", symbols: {} });
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_10.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_i16", 0n);
Deno.core.ops.op_ffi_read_i16(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_11.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_u32", 0n);
Deno.core.ops.op_ffi_read_u32(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_12.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_i32", 0n);
Deno.core.ops.op_ffi_read_i32(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_13.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_u64", 0n);
Deno.core.ops.op_ffi_read_u64(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_14.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_f32", 0n);
Deno.core.ops.op_ffi_read_f32(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_15.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_f64", 0n);
Deno.core.ops.op_ffi_read_f64(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Deno.core.opSync("op_ffi_call_ptr", 0n, {
Deno.core.ops.op_ffi_call_ptr(0n, {
name: null,
parameters: [],
result: "void",
Expand Down
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_4.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_ptr_of", new Uint8Array(0));
Deno.core.ops.op_ffi_ptr_of(new Uint8Array(0));
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_5.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_buf_copy_into", 0n, new Uint8Array(0), 0);
Deno.core.ops.op_ffi_buf_copy_into(0n, new Uint8Array(0), 0);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_6.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_cstr_read", 0n);
Deno.core.ops.op_ffi_cstr_read(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_7.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_u8", 0n);
Deno.core.ops.op_ffi_read_u8(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_8.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_i8", 0n);
Deno.core.ops.op_ffi_read_i8(0n);
2 changes: 1 addition & 1 deletion cli/tests/testdata/unstable_ffi_9.js
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Deno.core.opSync("op_ffi_read_u16", 0n);
Deno.core.ops.op_ffi_read_u16(0n);
2 changes: 1 addition & 1 deletion cli/tests/unit/opcall_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Deno.test(async function opsAsyncBadResource() {
Deno.test(function opsSyncBadResource() {
try {
const nonExistingRid = 9999;
Deno.core.opSync("op_read_sync", nonExistingRid, new Uint8Array(0));
Deno.core.ops.op_read_sync(nonExistingRid, new Uint8Array(0));
} catch (e) {
if (!(e instanceof Deno.errors.BadResource)) {
throw e;
Expand Down
32 changes: 15 additions & 17 deletions cli/tsc/99_main_compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ delete Object.prototype.__proto__;
((window) => {
/** @type {DenoCore} */
const core = window.Deno.core;
const ops = core.ops;

let logDebug = false;
let logSource = "JS";
Expand Down Expand Up @@ -250,7 +251,7 @@ delete Object.prototype.__proto__;
}

this.#lastCheckTimeMs = timeMs;
return core.opSync("op_is_cancelled", {});
return ops.op_is_cancelled();
}

throwIfCancellationRequested() {
Expand All @@ -274,11 +275,11 @@ delete Object.prototype.__proto__;
fileExists(specifier) {
debug(`host.fileExists("${specifier}")`);
specifier = normalizedToOriginalMap.get(specifier) ?? specifier;
return core.opSync("op_exists", { specifier });
return ops.op_exists({ specifier });
},
readFile(specifier) {
debug(`host.readFile("${specifier}")`);
return core.opSync("op_load", { specifier }).data;
return ops.op_load({ specifier }).data;
},
getCancellationToken() {
// createLanguageService will call this immediately and cache it
Expand Down Expand Up @@ -309,8 +310,7 @@ delete Object.prototype.__proto__;
}

/** @type {{ data: string; scriptKind: ts.ScriptKind; version: string; }} */
const { data, scriptKind, version } = core.opSync(
"op_load",
const { data, scriptKind, version } = ops.op_load(
{ specifier },
);
assert(
Expand Down Expand Up @@ -338,14 +338,13 @@ delete Object.prototype.__proto__;
},
writeFile(fileName, data, _writeByteOrderMark, _onError, _sourceFiles) {
debug(`host.writeFile("${fileName}")`);
return core.opSync(
"op_emit",
return ops.op_emit(
{ fileName, data },
);
},
getCurrentDirectory() {
debug(`host.getCurrentDirectory()`);
return cwd ?? core.opSync("op_cwd", null);
return cwd ?? ops.op_cwd();
},
getCanonicalFileName(fileName) {
return fileName;
Expand All @@ -361,7 +360,7 @@ delete Object.prototype.__proto__;
debug(` base: ${base}`);
debug(` specifiers: ${specifiers.join(", ")}`);
/** @type {Array<[string, ts.Extension] | undefined>} */
const resolved = core.opSync("op_resolve", {
const resolved = ops.op_resolve({
specifiers,
base,
});
Expand All @@ -384,7 +383,7 @@ delete Object.prototype.__proto__;
}
},
createHash(data) {
return core.opSync("op_create_hash", { data }).hash;
return ops.op_create_hash({ data }).hash;
},

// LanguageServiceHost
Expand All @@ -399,7 +398,7 @@ delete Object.prototype.__proto__;
if (scriptFileNamesCache) {
return scriptFileNamesCache;
}
return scriptFileNamesCache = core.opSync("op_script_names", undefined);
return scriptFileNamesCache = ops.op_script_names();
},
getScriptVersion(specifier) {
debug(`host.getScriptVersion("${specifier}")`);
Expand All @@ -412,7 +411,7 @@ delete Object.prototype.__proto__;
if (scriptVersionCache.has(specifier)) {
return scriptVersionCache.get(specifier);
}
const scriptVersion = core.opSync("op_script_version", { specifier });
const scriptVersion = ops.op_script_version({ specifier });
scriptVersionCache.set(specifier, scriptVersion);
return scriptVersion;
},
Expand All @@ -433,8 +432,7 @@ delete Object.prototype.__proto__;
};
}

const fileInfo = core.opSync(
"op_load",
const fileInfo = ops.op_load(
{ specifier },
);
if (fileInfo) {
Expand Down Expand Up @@ -567,7 +565,7 @@ delete Object.prototype.__proto__;

performanceProgram({ program });

core.opSync("op_respond", {
ops.op_respond({
diagnostics: fromTypeScriptDiagnostic(diagnostics),
stats: performanceEnd(),
});
Expand All @@ -579,7 +577,7 @@ delete Object.prototype.__proto__;
* @param {any} data
*/
function respond(id, data = null) {
core.opSync("op_respond", { id, data });
ops.op_respond({ id, data });
}

/**
Expand Down Expand Up @@ -942,7 +940,7 @@ delete Object.prototype.__proto__;
// ensure the snapshot is setup properly.
/** @type {{ buildSpecifier: string; libs: string[] }} */

const { buildSpecifier, libs } = core.opSync("op_build_info", {});
const { buildSpecifier, libs } = ops.op_build_info();
for (const lib of libs) {
const specifier = `lib.${lib}.d.ts`;
// we are using internal APIs here to "inject" our custom libraries into
Expand Down
5 changes: 3 additions & 2 deletions cli/tsc/compiler.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ declare global {
encode(value: string): Uint8Array;
// deno-lint-ignore no-explicit-any
opSync<T>(name: string, params: T): any;
ops(): void;
print(msg: string, stderr: bool): void;
// deno-lint-ignore no-explicit-any
ops: Record<string, (...args: unknown[]) => any>;
print(msg: string, stderr: boolean): void;
registerErrorClass(
name: string,
Ctor: typeof Error,
Expand Down
Loading

0 comments on commit 2164f6b

Please sign in to comment.