mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-25 20:14:21 +01:00
bindings/javascript: Reduce VM/native crossing overhead
Before: ``` penberg@vonneumann perf % node perf-turso.js cpu: Apple M1 runtime: node v22.16.0 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------- ----------------------------- • Statement ----------------------------------------------------------------------- ----------------------------- Statement.get() bind parameters 1'525 ns/iter (1'482 ns … 1'720 ns) 1'534 ns 1'662 ns 1'720 ns summary for Statement Statement.get() bind parameters penberg@vonneumann perf % bun perf-turso.js cpu: Apple M1 runtime: bun 1.2.15 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------- ----------------------------- • Statement ----------------------------------------------------------------------- ----------------------------- Statement.get() bind parameters 1'198 ns/iter (1'157 ns … 1'495 ns) 1'189 ns 1'456 ns 1'495 ns summary for Statement Statement.get() bind parameters ``` After: ``` benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------- ----------------------------- • Statement ----------------------------------------------------------------------- ----------------------------- Statement.get() bind parameters 1'206 ns/iter (1'180 ns … 1'402 ns) 1'208 ns 1'365 ns 1'402 ns summary for Statement Statement.get() bind parameters penberg@vonneumann perf % bun perf-turso.js cpu: Apple M1 runtime: bun 1.2.15 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p999 ----------------------------------------------------------------------- ----------------------------- • Statement ----------------------------------------------------------------------- ----------------------------- Statement.get() bind parameters 1'019 ns/iter (980 ns … 1'360 ns) 1'005 ns 1'270 ns 1'360 ns summary for Statement Statement.get() bind parameters ```
This commit is contained in:
@@ -15,6 +15,11 @@ use napi::{Env, Task};
|
||||
use napi_derive::napi;
|
||||
use std::{cell::RefCell, num::NonZeroUsize, sync::Arc};
|
||||
|
||||
/// Step result constants
|
||||
const STEP_ROW: u32 = 1;
|
||||
const STEP_DONE: u32 = 2;
|
||||
const STEP_IO: u32 = 3;
|
||||
|
||||
/// The presentation mode for rows.
|
||||
#[derive(Debug, Clone)]
|
||||
enum PresentationMode {
|
||||
@@ -289,92 +294,90 @@ impl Statement {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Step the statement and return result code:
|
||||
/// 1 = Row available, 2 = Done, 3 = I/O needed
|
||||
#[napi]
|
||||
pub fn step<'env>(&self, env: &'env Env) -> Result<Unknown<'env>> {
|
||||
pub fn step(&self) -> Result<u32> {
|
||||
let mut stmt_ref = self.stmt.borrow_mut();
|
||||
let stmt = stmt_ref
|
||||
.as_mut()
|
||||
.ok_or_else(|| Error::new(Status::GenericFailure, "Statement has been finalized"))?;
|
||||
|
||||
let mut result = Object::new(env)?;
|
||||
|
||||
match stmt.step() {
|
||||
Ok(turso_core::StepResult::Row) => {
|
||||
result.set_named_property("done", false)?;
|
||||
|
||||
let row_data = stmt
|
||||
.row()
|
||||
.ok_or_else(|| Error::new(Status::GenericFailure, "No row data available"))?;
|
||||
|
||||
let mode = self.mode.borrow();
|
||||
let row_value =
|
||||
match *mode {
|
||||
PresentationMode::Raw => {
|
||||
let mut raw_array = env.create_array(row_data.len() as u32)?;
|
||||
for (idx, value) in row_data.get_values().enumerate() {
|
||||
let js_value = to_js_value(env, value)?;
|
||||
raw_array.set(idx as u32, js_value)?;
|
||||
}
|
||||
raw_array.coerce_to_object()?.to_unknown()
|
||||
}
|
||||
PresentationMode::Pluck => {
|
||||
let (_, value) = row_data.get_values().enumerate().next().ok_or(
|
||||
napi::Error::new(
|
||||
napi::Status::GenericFailure,
|
||||
"Pluck mode requires at least one column in the result",
|
||||
),
|
||||
)?;
|
||||
to_js_value(env, value)?
|
||||
}
|
||||
PresentationMode::Expanded => {
|
||||
let row = Object::new(env)?;
|
||||
let raw_row = row.raw();
|
||||
let raw_env = env.raw();
|
||||
for idx in 0..row_data.len() {
|
||||
let value = row_data.get_value(idx);
|
||||
let column_name = &self.column_names[idx];
|
||||
let js_value = to_js_value(env, value)?;
|
||||
unsafe {
|
||||
napi::sys::napi_set_named_property(
|
||||
raw_env,
|
||||
raw_row,
|
||||
column_name.as_ptr(),
|
||||
js_value.raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
row.to_unknown()
|
||||
}
|
||||
};
|
||||
|
||||
result.set_named_property("value", row_value)?;
|
||||
}
|
||||
Ok(turso_core::StepResult::Done) => {
|
||||
result.set_named_property("done", true)?;
|
||||
result.set_named_property("value", Null)?;
|
||||
}
|
||||
Ok(turso_core::StepResult::IO) => {
|
||||
result.set_named_property("io", true)?;
|
||||
result.set_named_property("value", Null)?;
|
||||
}
|
||||
Ok(turso_core::StepResult::Row) => Ok(STEP_ROW),
|
||||
Ok(turso_core::StepResult::Done) => Ok(STEP_DONE),
|
||||
Ok(turso_core::StepResult::IO) => Ok(STEP_IO),
|
||||
Ok(turso_core::StepResult::Interrupt) => {
|
||||
return Err(Error::new(
|
||||
Err(Error::new(
|
||||
Status::GenericFailure,
|
||||
"Statement was interrupted",
|
||||
));
|
||||
))
|
||||
}
|
||||
Ok(turso_core::StepResult::Busy) => {
|
||||
return Err(Error::new(Status::GenericFailure, "Database is busy"));
|
||||
Err(Error::new(Status::GenericFailure, "Database is busy"))
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(Error::new(
|
||||
Err(Error::new(
|
||||
Status::GenericFailure,
|
||||
format!("Step failed: {e}"),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(result.to_unknown())
|
||||
/// Get the current row data according to the presentation mode
|
||||
#[napi]
|
||||
pub fn row<'env>(&self, env: &'env Env) -> Result<Unknown<'env>> {
|
||||
let stmt_ref = self.stmt.borrow();
|
||||
let stmt = stmt_ref
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::new(Status::GenericFailure, "Statement has been finalized"))?;
|
||||
|
||||
let row_data = stmt
|
||||
.row()
|
||||
.ok_or_else(|| Error::new(Status::GenericFailure, "No row data available"))?;
|
||||
|
||||
let mode = self.mode.borrow();
|
||||
let row_value = match *mode {
|
||||
PresentationMode::Raw => {
|
||||
let mut raw_array = env.create_array(row_data.len() as u32)?;
|
||||
for (idx, value) in row_data.get_values().enumerate() {
|
||||
let js_value = to_js_value(env, value)?;
|
||||
raw_array.set(idx as u32, js_value)?;
|
||||
}
|
||||
raw_array.coerce_to_object()?.to_unknown()
|
||||
}
|
||||
PresentationMode::Pluck => {
|
||||
let (_, value) = row_data.get_values().enumerate().next().ok_or(
|
||||
napi::Error::new(
|
||||
napi::Status::GenericFailure,
|
||||
"Pluck mode requires at least one column in the result",
|
||||
),
|
||||
)?;
|
||||
to_js_value(env, value)?
|
||||
}
|
||||
PresentationMode::Expanded => {
|
||||
let row = Object::new(env)?;
|
||||
let raw_row = row.raw();
|
||||
let raw_env = env.raw();
|
||||
for idx in 0..row_data.len() {
|
||||
let value = row_data.get_value(idx);
|
||||
let column_name = &self.column_names[idx];
|
||||
let js_value = to_js_value(env, value)?;
|
||||
unsafe {
|
||||
napi::sys::napi_set_named_property(
|
||||
raw_env,
|
||||
raw_row,
|
||||
column_name.as_ptr(),
|
||||
js_value.raw(),
|
||||
);
|
||||
}
|
||||
}
|
||||
row.to_unknown()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(row_value)
|
||||
}
|
||||
|
||||
/// Sets the presentation mode to raw.
|
||||
|
||||
Reference in New Issue
Block a user