mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 20:25:12 -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,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeSet,
|
||||
Uint32Array,
|
||||
// deno-lint-ignore camelcase
|
||||
NumberPOSITIVE_INFINITY,
|
||||
PromisePrototypeThen,
|
||||
|
@ -25,8 +26,14 @@
|
|||
const { reportException } = window.__bootstrap.event;
|
||||
const { assert } = window.__bootstrap.infra;
|
||||
|
||||
let hr;
|
||||
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_bench_util::bench_js_async;
|
||||
use deno_bench_util::bench_or_profile;
|
||||
use deno_bench_util::bencher::{benchmark_group, Bencher};
|
||||
use deno_bench_util::{bench_js_async, bench_js_sync};
|
||||
use deno_web::BlobStore;
|
||||
|
||||
struct Permissions;
|
||||
|
@ -27,7 +27,7 @@ fn setup() -> Vec<Extension> {
|
|||
Extension::builder()
|
||||
.js(vec![
|
||||
("setup", r#"
|
||||
const { opNow, setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
|
||||
const { setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
|
||||
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) {
|
||||
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);
|
||||
|
|
|
@ -51,6 +51,7 @@ pub use crate::message_port::JsMessageData;
|
|||
pub use crate::message_port::MessagePort;
|
||||
|
||||
use crate::timers::op_now;
|
||||
use crate::timers::op_now_set_buf;
|
||||
use crate::timers::op_sleep;
|
||||
use crate::timers::op_timer_handle;
|
||||
use crate::timers::StartTime;
|
||||
|
@ -106,6 +107,7 @@ pub fn init<P: TimersPermission + 'static>(
|
|||
compression::op_compression_new::decl(),
|
||||
compression::op_compression_write::decl(),
|
||||
compression::op_compression_finish::decl(),
|
||||
op_now_set_buf::decl(),
|
||||
op_now::decl::<P>(),
|
||||
op_timer_handle::decl(),
|
||||
op_cancel_handle::decl(),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::op;
|
||||
use deno_core::ZeroCopyBuf;
|
||||
|
||||
use deno_core::CancelFuture;
|
||||
use deno_core::CancelHandle;
|
||||
|
@ -23,29 +24,50 @@ pub trait TimersPermission {
|
|||
|
||||
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
|
||||
// since the start time of the deno runtime.
|
||||
// If the High precision flag is not set, the
|
||||
// nanoseconds are rounded on 2ms.
|
||||
#[op]
|
||||
pub fn op_now<TP>(state: &mut OpState) -> f64
|
||||
#[op(fast)]
|
||||
pub fn op_now<TP>(state: &mut OpState)
|
||||
where
|
||||
TP: TimersPermission + 'static,
|
||||
{
|
||||
let start_time = state.borrow::<StartTime>();
|
||||
let elapsed = start_time.elapsed();
|
||||
let seconds = elapsed.as_secs();
|
||||
let mut subsec_nanos = elapsed.subsec_nanos() as f64;
|
||||
let reduced_time_precision = 2_000_000.0; // 2ms in nanoseconds
|
||||
let mut subsec_nanos = elapsed.subsec_nanos();
|
||||
|
||||
// If the permission is not enabled
|
||||
// Round the nano result on 2 milliseconds
|
||||
// see: https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp#Reduced_time_precision
|
||||
if !state.borrow_mut::<TP>().allow_hrtime() {
|
||||
let reduced_time_precision = 2_000_000; // 2ms in nanoseconds
|
||||
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>);
|
||||
|
|
30
ops/lib.rs
30
ops/lib.rs
|
@ -365,15 +365,32 @@ fn codegen_fast_impl(
|
|||
}
|
||||
},
|
||||
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 (
|
||||
quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
#[doc(hidden)]
|
||||
struct #fast_struct #ty_generics {
|
||||
_phantom: ::std::marker::PhantomData<#type_params>,
|
||||
}
|
||||
#trampoline
|
||||
impl #impl_generics #core::v8::fast_api::FastFunction for #name #ty_generics {
|
||||
fn function(&self) -> *const ::std::ffi::c_void {
|
||||
impl #impl_generics #core::v8::fast_api::FastFunction for #fast_struct #ty_generics #where_clause {
|
||||
fn function(&self) -> *const ::std::ffi::c_void {
|
||||
#raw_block
|
||||
}
|
||||
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> {
|
||||
// TODO(@littledivy): Support generics
|
||||
if !f.sig.generics.params.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inputs = &f.sig.inputs;
|
||||
let ret = match &f.sig.output {
|
||||
syn::ReturnType::Default => quote!(#core::v8::fast_api::CType::Void),
|
||||
|
|
Loading…
Add table
Reference in a new issue