mirror of
https://github.com/denoland/deno.git
synced 2025-02-08 07:16:56 -05:00
perf: use fast api for op_now (#15643)
This commit is contained in:
parent
7c4f57e8b0
commit
d8396225c4
6 changed files with 80 additions and 22 deletions
19
cli/bench/op_now.js
Normal file
19
cli/bench/op_now.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
|
||||||
|
const queueMicrotask = globalThis.queueMicrotask || process.nextTick;
|
||||||
|
let [total, count] = typeof Deno !== "undefined"
|
||||||
|
? Deno.args
|
||||||
|
: [process.argv[2], process.argv[3]];
|
||||||
|
|
||||||
|
total = total ? parseInt(total, 0) : 50;
|
||||||
|
count = count ? parseInt(count, 10) : 10000000;
|
||||||
|
|
||||||
|
function bench(fun) {
|
||||||
|
const start = Date.now();
|
||||||
|
for (let i = 0; i < count; i++) fun();
|
||||||
|
const elapsed = Date.now() - start;
|
||||||
|
const rate = Math.floor(count / (elapsed / 1000));
|
||||||
|
console.log(`time ${elapsed} ms rate ${rate}`);
|
||||||
|
if (--total) queueMicrotask(() => bench(fun));
|
||||||
|
}
|
||||||
|
|
||||||
|
bench(() => performance.now());
|
|
@ -13,6 +13,7 @@
|
||||||
MapPrototypeGet,
|
MapPrototypeGet,
|
||||||
MapPrototypeHas,
|
MapPrototypeHas,
|
||||||
MapPrototypeSet,
|
MapPrototypeSet,
|
||||||
|
Uint32Array,
|
||||||
// deno-lint-ignore camelcase
|
// deno-lint-ignore camelcase
|
||||||
NumberPOSITIVE_INFINITY,
|
NumberPOSITIVE_INFINITY,
|
||||||
PromisePrototypeThen,
|
PromisePrototypeThen,
|
||||||
|
@ -25,8 +26,14 @@
|
||||||
const { reportException } = window.__bootstrap.event;
|
const { reportException } = window.__bootstrap.event;
|
||||||
const { assert } = window.__bootstrap.infra;
|
const { assert } = window.__bootstrap.infra;
|
||||||
|
|
||||||
|
let hr;
|
||||||
function opNow() {
|
function opNow() {
|
||||||
return ops.op_now();
|
if (!hr) {
|
||||||
|
hr = new Uint32Array(2);
|
||||||
|
ops.op_now_set_buf(hr);
|
||||||
|
}
|
||||||
|
ops.op_now.fast();
|
||||||
|
return (hr[0] * 1000 + hr[1] / 1e6);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use deno_core::Extension;
|
use deno_core::Extension;
|
||||||
|
|
||||||
|
use deno_bench_util::bench_js_async;
|
||||||
use deno_bench_util::bench_or_profile;
|
use deno_bench_util::bench_or_profile;
|
||||||
use deno_bench_util::bencher::{benchmark_group, Bencher};
|
use deno_bench_util::bencher::{benchmark_group, Bencher};
|
||||||
use deno_bench_util::{bench_js_async, bench_js_sync};
|
|
||||||
use deno_web::BlobStore;
|
use deno_web::BlobStore;
|
||||||
|
|
||||||
struct Permissions;
|
struct Permissions;
|
||||||
|
@ -27,7 +27,7 @@ fn setup() -> Vec<Extension> {
|
||||||
Extension::builder()
|
Extension::builder()
|
||||||
.js(vec![
|
.js(vec![
|
||||||
("setup", r#"
|
("setup", r#"
|
||||||
const { opNow, setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
|
const { setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
|
||||||
Deno.core.setMacrotaskCallback(handleTimerMacrotask);
|
Deno.core.setMacrotaskCallback(handleTimerMacrotask);
|
||||||
"#),
|
"#),
|
||||||
])
|
])
|
||||||
|
@ -39,13 +39,9 @@ fn setup() -> Vec<Extension> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_op_now(b: &mut Bencher) {
|
|
||||||
bench_js_sync(b, r#"opNow();"#, setup);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_set_timeout(b: &mut Bencher) {
|
fn bench_set_timeout(b: &mut Bencher) {
|
||||||
bench_js_async(b, r#"setTimeout(() => {}, 0);"#, setup);
|
bench_js_async(b, r#"setTimeout(() => {}, 0);"#, setup);
|
||||||
}
|
}
|
||||||
|
|
||||||
benchmark_group!(benches, bench_op_now, bench_set_timeout,);
|
benchmark_group!(benches, bench_set_timeout,);
|
||||||
bench_or_profile!(benches);
|
bench_or_profile!(benches);
|
||||||
|
|
|
@ -51,6 +51,7 @@ pub use crate::message_port::JsMessageData;
|
||||||
pub use crate::message_port::MessagePort;
|
pub use crate::message_port::MessagePort;
|
||||||
|
|
||||||
use crate::timers::op_now;
|
use crate::timers::op_now;
|
||||||
|
use crate::timers::op_now_set_buf;
|
||||||
use crate::timers::op_sleep;
|
use crate::timers::op_sleep;
|
||||||
use crate::timers::op_timer_handle;
|
use crate::timers::op_timer_handle;
|
||||||
use crate::timers::StartTime;
|
use crate::timers::StartTime;
|
||||||
|
@ -106,6 +107,7 @@ pub fn init<P: TimersPermission + 'static>(
|
||||||
compression::op_compression_new::decl(),
|
compression::op_compression_new::decl(),
|
||||||
compression::op_compression_write::decl(),
|
compression::op_compression_write::decl(),
|
||||||
compression::op_compression_finish::decl(),
|
compression::op_compression_finish::decl(),
|
||||||
|
op_now_set_buf::decl(),
|
||||||
op_now::decl::<P>(),
|
op_now::decl::<P>(),
|
||||||
op_timer_handle::decl(),
|
op_timer_handle::decl(),
|
||||||
op_cancel_handle::decl(),
|
op_cancel_handle::decl(),
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::op;
|
use deno_core::op;
|
||||||
|
use deno_core::ZeroCopyBuf;
|
||||||
|
|
||||||
use deno_core::CancelFuture;
|
use deno_core::CancelFuture;
|
||||||
use deno_core::CancelHandle;
|
use deno_core::CancelHandle;
|
||||||
|
@ -23,29 +24,50 @@ pub trait TimersPermission {
|
||||||
|
|
||||||
pub type StartTime = Instant;
|
pub type StartTime = Instant;
|
||||||
|
|
||||||
|
static mut NOW_BUF: *mut u32 = std::ptr::null_mut();
|
||||||
|
|
||||||
|
#[op]
|
||||||
|
pub fn op_now_set_buf(buf: ZeroCopyBuf) {
|
||||||
|
assert_eq!(buf.len(), 8);
|
||||||
|
// SAFETY: This is safe because this is the only place where we initialize
|
||||||
|
// NOW_BUF.
|
||||||
|
unsafe {
|
||||||
|
NOW_BUF = buf.as_ptr() as *mut u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a milliseconds and nanoseconds subsec
|
// Returns a milliseconds and nanoseconds subsec
|
||||||
// since the start time of the deno runtime.
|
// since the start time of the deno runtime.
|
||||||
// If the High precision flag is not set, the
|
// If the High precision flag is not set, the
|
||||||
// nanoseconds are rounded on 2ms.
|
// nanoseconds are rounded on 2ms.
|
||||||
#[op]
|
#[op(fast)]
|
||||||
pub fn op_now<TP>(state: &mut OpState) -> f64
|
pub fn op_now<TP>(state: &mut OpState)
|
||||||
where
|
where
|
||||||
TP: TimersPermission + 'static,
|
TP: TimersPermission + 'static,
|
||||||
{
|
{
|
||||||
let start_time = state.borrow::<StartTime>();
|
let start_time = state.borrow::<StartTime>();
|
||||||
let elapsed = start_time.elapsed();
|
let elapsed = start_time.elapsed();
|
||||||
let seconds = elapsed.as_secs();
|
let seconds = elapsed.as_secs();
|
||||||
let mut subsec_nanos = elapsed.subsec_nanos() as f64;
|
let mut subsec_nanos = elapsed.subsec_nanos();
|
||||||
let reduced_time_precision = 2_000_000.0; // 2ms in nanoseconds
|
|
||||||
|
|
||||||
// If the permission is not enabled
|
// If the permission is not enabled
|
||||||
// Round the nano result on 2 milliseconds
|
// Round the nano result on 2 milliseconds
|
||||||
// see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
|
// see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
|
||||||
if !state.borrow_mut::<TP>().allow_hrtime() {
|
if !state.borrow_mut::<TP>().allow_hrtime() {
|
||||||
|
let reduced_time_precision = 2_000_000; // 2ms in nanoseconds
|
||||||
subsec_nanos -= subsec_nanos % reduced_time_precision;
|
subsec_nanos -= subsec_nanos % reduced_time_precision;
|
||||||
}
|
}
|
||||||
|
|
||||||
(seconds * 1_000) as f64 + (subsec_nanos / 1_000_000.0)
|
// SAFETY: This is safe because we initialize NOW_BUF in op_now_set_buf, its a null pointer
|
||||||
|
// otherwise.
|
||||||
|
// op_now_set_buf guarantees that the buffer is 8 bytes long.
|
||||||
|
unsafe {
|
||||||
|
if !NOW_BUF.is_null() {
|
||||||
|
let buf = std::slice::from_raw_parts_mut(NOW_BUF, 2);
|
||||||
|
buf[0] = seconds as u32;
|
||||||
|
buf[1] = subsec_nanos as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TimerHandle(Rc<CancelHandle>);
|
pub struct TimerHandle(Rc<CancelHandle>);
|
||||||
|
|
30
ops/lib.rs
30
ops/lib.rs
|
@ -365,15 +365,32 @@ fn codegen_fast_impl(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote! {
|
quote! {
|
||||||
#func_name #ty_generics as *const _
|
#func_name::<#type_params> as *const _
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let fast_struct = format_ident!("fast_{}", name);
|
||||||
|
let (type_params, ty_generics, struct_generics) =
|
||||||
|
if type_params.is_empty() {
|
||||||
|
(quote! { () }, quote! {}, quote! {})
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
quote! { #type_params },
|
||||||
|
quote! { #ty_generics },
|
||||||
|
quote! { ::<#type_params> },
|
||||||
|
)
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
quote! {
|
quote! {
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[doc(hidden)]
|
||||||
|
struct #fast_struct #ty_generics {
|
||||||
|
_phantom: ::std::marker::PhantomData<#type_params>,
|
||||||
|
}
|
||||||
#trampoline
|
#trampoline
|
||||||
impl #impl_generics #core::v8::fast_api::FastFunction for #name #ty_generics {
|
impl #impl_generics #core::v8::fast_api::FastFunction for #fast_struct #ty_generics #where_clause {
|
||||||
fn function(&self) -> *const ::std::ffi::c_void {
|
fn function(&self) -> *const ::std::ffi::c_void {
|
||||||
#raw_block
|
#raw_block
|
||||||
}
|
}
|
||||||
fn args(&self) -> &'static [#core::v8::fast_api::Type] {
|
fn args(&self) -> &'static [#core::v8::fast_api::Type] {
|
||||||
|
@ -384,7 +401,7 @@ fn codegen_fast_impl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
quote! { Some(Box::new(#name #ty_generics)) },
|
quote! { Some(Box::new(#fast_struct #struct_generics { _phantom: ::std::marker::PhantomData })) },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -439,11 +456,6 @@ struct FastApiSyn {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_be_fast_api(core: &TokenStream2, f: &syn::ItemFn) -> Option<FastApiSyn> {
|
fn can_be_fast_api(core: &TokenStream2, f: &syn::ItemFn) -> Option<FastApiSyn> {
|
||||||
// TODO(@littledivy): Support generics
|
|
||||||
if !f.sig.generics.params.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let inputs = &f.sig.inputs;
|
let inputs = &f.sig.inputs;
|
||||||
let ret = match &f.sig.output {
|
let ret = match &f.sig.output {
|
||||||
syn::ReturnType::Default => quote!(#core::v8::fast_api::CType::Void),
|
syn::ReturnType::Default => quote!(#core::v8::fast_api::CType::Void),
|
||||||
|
|
Loading…
Add table
Reference in a new issue