mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-30 22:44:21 +01:00
Merge 'VDBE with indirect function dispatch' from Pere Diaz Bou
This PR is unapologetically stolen from @vmg's implementation in Vitess implemented here https://github.com/vitessio/vitess/pull/12369. If you want a more in depth explanation of how this works you can read the [blog post he carefully wrote](https://planetscale.com/blog/faster-interpreters-in-go-catching- up-with-cpp). In limbo we have a huge problem with [register spilling](https://en.wikipedia.org/wiki/Register_allocation), this can be easily observed with the prolog of `Program::step` before: ```llvm start: %e.i.i304.i = alloca [0 x i8], align 8 %formatter.i305.i = alloca [64 x i8], align 8 %buf.i306.i = alloca [24 x i8], align 8 %formatter.i259.i = alloca [64 x i8], align 8 ..................... these are repeated for hundreds of lines ..................... %formatter.i52.i = alloca [64 x i8], align 8 %buf.i53.i = alloca [24 x i8], align 8 %formatter.i.i = alloca [64 x i8], align 8 %buf.i.i = alloca [24 x i8], align 8 %_87.i = alloca [48 x i8], align 8 %_82.i = alloca [24 x i8], align 8 %_73.i = alloca [24 x i8], align 8 %_66.i8446 = alloca [24 x i8], align 8 %_57.i = alloca [24 x i8], align 8 %_48.i = alloca [24 x i8], align 8 ``` After these changes we completely remove the need of register spilling (yes that is the complete prolog): ```llvm start: %self1 = alloca [80 x i8], align 8 %pager = alloca [8 x i8], align 8 %mv_store = alloca [8 x i8], align 8 store ptr %0, ptr %mv_store, align 8 store ptr %1, ptr %pager, align 8 %2 = getelementptr inbounds i8, ptr %state, i64 580 %3 = getelementptr inbounds i8, ptr %state, i64 576 %4 = getelementptr inbounds i8, ptr %self, i64 16 %5 = getelementptr inbounds i8, ptr %self, i64 8 %6 = getelementptr inbounds i8, ptr %self1, i64 8 br label %bb1, !dbg !286780 ``` When it comes to branch prediction, we don't really fix a lot because thankfully rust already compiles `match` expressions to a jump table: ```llvm %insn = getelementptr inbounds [0 x %"vdbe::insn::Insn"], ptr %self657, i64 0, i64 %index, !dbg !249527 %332 = load i8, ptr %insn, align 8, !dbg !249528, !range !38291, !noundef !14 switch i8 %332, label %default.unreachable26674 [ i8 0, label %bb111 i8 1, label %bb101 i8 2, label %bb100 i8 3, label %bb110 ... i8 104, label %bb5 i8 105, label %bb16 i8 106, label %bb14 ], !dbg !249530 ``` Some results ---- ``` function dispatch: Execute `SELECT 1`/limbo_execute_select_1 time: [29.498 ns 29.548 ns 29.601 ns] change: [-3.6125% -3.3592% -3.0804%] (p = 0.00 < 0.05) main: Execute `SELECT 1`/limbo_execute_select_1 time: [33.789 ns 33.832 ns 33.878 ns] ``` Closes #1233
This commit is contained in:
@@ -16,14 +16,14 @@ use crate::{
|
||||
Connection, VirtualTable,
|
||||
};
|
||||
|
||||
use super::{BranchOffset, CursorID, Insn, InsnReference, Program};
|
||||
use super::{BranchOffset, CursorID, Insn, InsnFunction, InsnReference, Program};
|
||||
#[allow(dead_code)]
|
||||
pub struct ProgramBuilder {
|
||||
next_free_register: usize,
|
||||
next_free_cursor_id: usize,
|
||||
insns: Vec<Insn>,
|
||||
insns: Vec<(Insn, InsnFunction)>,
|
||||
// for temporarily storing instructions that will be put after Transaction opcode
|
||||
constant_insns: Vec<Insn>,
|
||||
constant_insns: Vec<(Insn, InsnFunction)>,
|
||||
// Vector of labels which must be assigned to next emitted instruction
|
||||
next_insn_labels: Vec<BranchOffset>,
|
||||
// Cursors that are referenced by the program. Indexed by CursorID.
|
||||
@@ -127,7 +127,8 @@ impl ProgramBuilder {
|
||||
self.label_to_resolved_offset[label.to_label_value() as usize] =
|
||||
Some(self.insns.len() as InsnReference);
|
||||
}
|
||||
self.insns.push(insn);
|
||||
let function = insn.to_function();
|
||||
self.insns.push((insn, function));
|
||||
}
|
||||
|
||||
pub fn emit_string8(&mut self, value: String, dest: usize) {
|
||||
@@ -253,7 +254,7 @@ impl ProgramBuilder {
|
||||
);
|
||||
}
|
||||
};
|
||||
for insn in self.insns.iter_mut() {
|
||||
for (insn, _) in self.insns.iter_mut() {
|
||||
match insn {
|
||||
Insn::Init { target_pc } => {
|
||||
resolve(target_pc, "Init");
|
||||
|
||||
5926
core/vdbe/execute.rs
Normal file
5926
core/vdbe/execute.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,8 @@
|
||||
use std::num::NonZero;
|
||||
|
||||
use super::{cast_text_to_numeric, AggFunc, BranchOffset, CursorID, FuncCtx, PageIdx};
|
||||
use super::{
|
||||
cast_text_to_numeric, execute, AggFunc, BranchOffset, CursorID, FuncCtx, InsnFunction, PageIdx,
|
||||
};
|
||||
use crate::storage::wal::CheckpointMode;
|
||||
use crate::types::{OwnedValue, Record};
|
||||
use limbo_macros::Description;
|
||||
@@ -1140,6 +1142,171 @@ pub fn exec_or(lhs: &OwnedValue, rhs: &OwnedValue) -> OwnedValue {
|
||||
}
|
||||
}
|
||||
|
||||
impl Insn {
|
||||
pub fn to_function(&self) -> InsnFunction {
|
||||
match self {
|
||||
Insn::Init { .. } => execute::execute_insn_init,
|
||||
|
||||
Insn::Null { .. } => execute::execute_insn_null,
|
||||
|
||||
Insn::NullRow { .. } => execute::execute_insn_null_row,
|
||||
|
||||
Insn::Add { .. } => execute::execute_insn_add,
|
||||
|
||||
Insn::Subtract { .. } => execute::execute_insn_subtract,
|
||||
|
||||
Insn::Multiply { .. } => execute::execute_insn_multiply,
|
||||
|
||||
Insn::Divide { .. } => execute::execute_insn_divide,
|
||||
|
||||
Insn::Compare { .. } => execute::execute_insn_compare,
|
||||
Insn::BitAnd { .. } => execute::execute_insn_bit_and,
|
||||
|
||||
Insn::BitOr { .. } => execute::execute_insn_bit_or,
|
||||
|
||||
Insn::BitNot { .. } => execute::execute_insn_bit_not,
|
||||
|
||||
Insn::Checkpoint { .. } => execute::execute_insn_checkpoint,
|
||||
Insn::Remainder { .. } => execute::execute_insn_remainder,
|
||||
|
||||
Insn::Jump { .. } => execute::execute_insn_jump,
|
||||
Insn::Move { .. } => execute::execute_insn_move,
|
||||
Insn::IfPos { .. } => execute::execute_insn_if_pos,
|
||||
Insn::NotNull { .. } => execute::execute_insn_not_null,
|
||||
|
||||
Insn::Eq { .. } => execute::execute_insn_eq,
|
||||
Insn::Ne { .. } => execute::execute_insn_ne,
|
||||
Insn::Lt { .. } => execute::execute_insn_lt,
|
||||
Insn::Le { .. } => execute::execute_insn_le,
|
||||
Insn::Gt { .. } => execute::execute_insn_gt,
|
||||
Insn::Ge { .. } => execute::execute_insn_ge,
|
||||
Insn::If { .. } => execute::execute_insn_if,
|
||||
Insn::IfNot { .. } => execute::execute_insn_if_not,
|
||||
Insn::OpenReadAsync { .. } => execute::execute_insn_open_read_async,
|
||||
Insn::OpenReadAwait => execute::execute_insn_open_read_await,
|
||||
|
||||
Insn::VOpenAsync { .. } => execute::execute_insn_vopen_async,
|
||||
|
||||
Insn::VOpenAwait => execute::execute_insn_vopen_await,
|
||||
|
||||
Insn::VCreate { .. } => execute::execute_insn_vcreate,
|
||||
Insn::VFilter { .. } => execute::execute_insn_vfilter,
|
||||
Insn::VColumn { .. } => execute::execute_insn_vcolumn,
|
||||
Insn::VUpdate { .. } => execute::execute_insn_vupdate,
|
||||
Insn::VNext { .. } => execute::execute_insn_vnext,
|
||||
Insn::OpenPseudo { .. } => execute::execute_insn_open_pseudo,
|
||||
Insn::RewindAsync { .. } => execute::execute_insn_rewind_async,
|
||||
|
||||
Insn::RewindAwait { .. } => execute::execute_insn_rewind_await,
|
||||
Insn::LastAsync { .. } => execute::execute_insn_last_async,
|
||||
|
||||
Insn::LastAwait { .. } => execute::execute_insn_last_await,
|
||||
Insn::Column { .. } => execute::execute_insn_column,
|
||||
Insn::MakeRecord { .. } => execute::execute_insn_make_record,
|
||||
Insn::ResultRow { .. } => execute::execute_insn_result_row,
|
||||
|
||||
Insn::NextAsync { .. } => execute::execute_insn_next_async,
|
||||
|
||||
Insn::NextAwait { .. } => execute::execute_insn_next_await,
|
||||
Insn::PrevAsync { .. } => execute::execute_insn_prev_async,
|
||||
|
||||
Insn::PrevAwait { .. } => execute::execute_insn_prev_await,
|
||||
Insn::Halt { .. } => execute::execute_insn_halt,
|
||||
Insn::Transaction { .. } => execute::execute_insn_transaction,
|
||||
|
||||
Insn::AutoCommit { .. } => execute::execute_insn_auto_commit,
|
||||
Insn::Goto { .. } => execute::execute_insn_goto,
|
||||
|
||||
Insn::Gosub { .. } => execute::execute_insn_gosub,
|
||||
Insn::Return { .. } => execute::execute_insn_return,
|
||||
|
||||
Insn::Integer { .. } => execute::execute_insn_integer,
|
||||
|
||||
Insn::Real { .. } => execute::execute_insn_real,
|
||||
|
||||
Insn::RealAffinity { .. } => execute::execute_insn_real_affinity,
|
||||
|
||||
Insn::String8 { .. } => execute::execute_insn_string8,
|
||||
|
||||
Insn::Blob { .. } => execute::execute_insn_blob,
|
||||
|
||||
Insn::RowId { .. } => execute::execute_insn_row_id,
|
||||
|
||||
Insn::SeekRowid { .. } => execute::execute_insn_seek_rowid,
|
||||
Insn::DeferredSeek { .. } => execute::execute_insn_deferred_seek,
|
||||
Insn::SeekGE { .. } => execute::execute_insn_seek_ge,
|
||||
Insn::SeekGT { .. } => execute::execute_insn_seek_gt,
|
||||
Insn::IdxGE { .. } => execute::execute_insn_idx_ge,
|
||||
Insn::IdxGT { .. } => execute::execute_insn_idx_gt,
|
||||
Insn::IdxLE { .. } => execute::execute_insn_idx_le,
|
||||
Insn::IdxLT { .. } => execute::execute_insn_idx_lt,
|
||||
Insn::DecrJumpZero { .. } => execute::execute_insn_decr_jump_zero,
|
||||
|
||||
Insn::AggStep { .. } => execute::execute_insn_agg_step,
|
||||
Insn::AggFinal { .. } => execute::execute_insn_agg_final,
|
||||
|
||||
Insn::SorterOpen { .. } => execute::execute_insn_sorter_open,
|
||||
Insn::SorterInsert { .. } => execute::execute_insn_sorter_insert,
|
||||
Insn::SorterSort { .. } => execute::execute_insn_sorter_sort,
|
||||
Insn::SorterData { .. } => execute::execute_insn_sorter_data,
|
||||
Insn::SorterNext { .. } => execute::execute_insn_sorter_next,
|
||||
Insn::Function { .. } => execute::execute_insn_function,
|
||||
Insn::InitCoroutine { .. } => execute::execute_insn_init_coroutine,
|
||||
Insn::EndCoroutine { .. } => execute::execute_insn_end_coroutine,
|
||||
|
||||
Insn::Yield { .. } => execute::execute_insn_yield,
|
||||
Insn::InsertAsync { .. } => execute::execute_insn_insert_async,
|
||||
Insn::InsertAwait { .. } => execute::execute_insn_insert_await,
|
||||
|
||||
Insn::DeleteAsync { .. } => execute::execute_insn_delete_async,
|
||||
|
||||
Insn::DeleteAwait { .. } => execute::execute_insn_delete_await,
|
||||
|
||||
Insn::NewRowid { .. } => execute::execute_insn_new_rowid,
|
||||
Insn::MustBeInt { .. } => execute::execute_insn_must_be_int,
|
||||
|
||||
Insn::SoftNull { .. } => execute::execute_insn_soft_null,
|
||||
|
||||
Insn::NotExists { .. } => execute::execute_insn_not_exists,
|
||||
Insn::OffsetLimit { .. } => execute::execute_insn_offset_limit,
|
||||
Insn::OpenWriteAsync { .. } => execute::execute_insn_open_write_async,
|
||||
Insn::OpenWriteAwait { .. } => execute::execute_insn_open_write_await,
|
||||
|
||||
Insn::Copy { .. } => execute::execute_insn_copy,
|
||||
Insn::CreateBtree { .. } => execute::execute_insn_create_btree,
|
||||
|
||||
Insn::Destroy { .. } => execute::execute_insn_destroy,
|
||||
Insn::DropTable { .. } => execute::execute_insn_drop_table,
|
||||
Insn::Close { .. } => execute::execute_insn_close,
|
||||
|
||||
Insn::IsNull { .. } => execute::execute_insn_is_null,
|
||||
|
||||
Insn::ParseSchema { .. } => execute::execute_insn_parse_schema,
|
||||
|
||||
Insn::ShiftRight { .. } => execute::execute_insn_shift_right,
|
||||
|
||||
Insn::ShiftLeft { .. } => execute::execute_insn_shift_left,
|
||||
|
||||
Insn::Variable { .. } => execute::execute_insn_variable,
|
||||
|
||||
Insn::ZeroOrNull { .. } => execute::execute_insn_zero_or_null,
|
||||
|
||||
Insn::Not { .. } => execute::execute_insn_not,
|
||||
|
||||
Insn::Concat { .. } => execute::execute_insn_concat,
|
||||
|
||||
Insn::And { .. } => execute::execute_insn_and,
|
||||
|
||||
Insn::Or { .. } => execute::execute_insn_or,
|
||||
|
||||
Insn::Noop => execute::execute_insn_noop,
|
||||
Insn::PageCount { .. } => execute::execute_insn_page_count,
|
||||
|
||||
Insn::ReadCookie { .. } => execute::execute_insn_read_cookie,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
|
||||
5018
core/vdbe/mod.rs
5018
core/vdbe/mod.rs
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user