0
0
Fork 0
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:
Divy Srivastava 2022-08-28 12:21:49 +05:30 committed by GitHub
parent 7c4f57e8b0
commit d8396225c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 22 deletions

19
cli/bench/op_now.js Normal file
View 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());

View file

@ -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);
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View file

@ -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);

View file

@ -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(),

View file

@ -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>);

View file

@ -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),