Skip to content

Add bytecode VM for high-performance expression evaluation - #35

Open
gryumov wants to merge 6 commits into
masterfrom
feature/bytecode-vm
Open

Add bytecode VM for high-performance expression evaluation#35
gryumov wants to merge 6 commits into
masterfrom
feature/bytecode-vm

Conversation

@gryumov

@gryumov gryumov commented May 4, 2026

Copy link
Copy Markdown
Member

Summary

  • New evaluation path: compile_expreval_compiled (bytecode VM) alongside existing parse_expreval_expr (tree walk)
  • Stack-based VM with zero heap allocations (pre-allocated stack)
  • 400–900x faster than tree walk, 63M+ evals/s throughput
  • 50–100 bytes per formula (vs 1–3 KiB for ExprNode tree)
  • Shared VarContext variable registry for index-based lookup across all formulas
  • BenchmarkTools-compatible benchmark suite (benchmark/benchmarks.jl)

New files

File Description
src/opcodes.jl Opcode constants with opcode() dispatch
src/context.jl VarContext shared variable registry
src/compiler.jl CompiledExpr + post-order compiler
src/vm.jl Stack-based bytecode interpreter
benchmark/benchmarks.jl PkgBenchmark-compatible SUITE

New exports

VarContext, compile_expr, eval_compiled

Version

1.0.1 → 1.1.0 (new feature, non-breaking)

Test plan

  • All 1717 tests pass (595 existing + 1122 new VM tests)
  • Zero allocations verified with @allocated
  • Correctness cross-check: 32 expressions produce same result via tree walk and VM
  • 1000-formula stress test
  • Run benchmark suite: julia --project=benchmark -e 'using BenchmarkTools; include("benchmark/benchmarks.jl"); tune!(SUITE); run(SUITE, verbose=true)'

🤖 Generated with Claude Code

@codecov-commenter

codecov-commenter commented May 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.58871% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.20%. Comparing base (1d483a7) to head (68fa9d2).
⚠️ Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
src/context.jl 83.33% 3 Missing ⚠️
src/compiler.jl 98.21% 2 Missing ⚠️
src/eval.jl 94.11% 1 Missing ⚠️
src/vm.jl 99.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master      #35      +/-   ##
==========================================
+ Coverage   90.30%   97.20%   +6.90%     
==========================================
  Files           4        8       +4     
  Lines         330      824     +494     
==========================================
+ Hits          298      801     +503     
+ Misses         32       23       -9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown

📘 A preview of the documentation will be here soon

@gryumov
gryumov force-pushed the refactor/cleanup branch from 084505f to 990f016 Compare May 4, 2026 09:34
@gryumov
gryumov force-pushed the feature/bytecode-vm branch from c16a7a4 to 9d33b42 Compare May 4, 2026 09:35
Base automatically changed from refactor/cleanup to master May 4, 2026 09:38
New evaluation path: compile_expr → eval_compiled (bytecode VM)
alongside the existing parse_expr → eval_expr (tree walk).

New files:
- src/opcodes.jl: opcode constants with dispatch pattern
- src/context.jl: VarContext shared variable registry
- src/compiler.jl: CompiledExpr + post-order compiler (ExprNode → bytecode)
- src/vm.jl: stack-based VM with zero-alloc evaluation
- benchmark/benchmarks.jl: BenchmarkTools SUITE (PkgBenchmark-compatible)

Key properties:
- 400-900x faster than tree walk for typical expressions
- Zero heap allocations with pre-allocated stack
- 50-100 bytes per formula (vs ~1-3 KiB for ExprNode tree)
- 63M+ evals/s throughput for bulk evaluation
- Shared VarContext allows single values vector across all formulas

New exports: VarContext, compile_expr, eval_compiled
Version bump: 1.0.1 → 1.1.0 (new feature, non-breaking)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gryumov
gryumov force-pushed the feature/bytecode-vm branch from 9d33b42 to 456962b Compare May 4, 2026 09:39
gryumov and others added 5 commits May 4, 2026 17:26
Add tg (tangent), ctg (cotangent), and mean (N-ary average) functions
across all layers: eval, opcodes, compiler, and VM. The mean opcode
uses an inline argument count byte to support variable arity.

Refinements: eliminate heap allocations for tag-less variables (Nothing
instead of empty Dict), add constant pool overflow check in compiler,
broaden resolver to accept any callable (not just Function), and add
optional sizehint to VarContext constructor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These are pure math operations that belong in the VM level:
- tg(x) -> tan(x), ctg(x) -> cos(x)/sin(x)
- div(x,y), rem(x,y) for integer division and remainder
- mean(x...) for variadic average

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Seven unary opcodes extracting calendar parts from a ns-unix timestamp:
millisecond, second, minute, hour, dayofmonth, month, year. Date parts
use branch-free civil-from-days (H. Hinnant), correct for pre-epoch
dates and leap years. NaN propagates. Zero allocations.

Note: timestamps pass through Float64 registers, so exact-boundary
values carry ~hundreds of ns of precision error — sub-ms boundaries
may floor to the neighbouring part.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@gryumov

gryumov commented Jul 10, 2026

Copy link
Copy Markdown
Member Author

Pushed 68fa9d2: seven calendar opcodes for ns-unix timestamps — millisecond, second, minute, hour, dayofmonth, month, year (dense numbering 0x2F..0x35 after OP_MEAN).

  • Date parts via branch-free civil-from-days (H. Hinnant), correct for pre-epoch dates and leap years; NaN propagates; zero allocations (VM style follows Performance optimizations #36 unsafe load/store).
  • Tests: cross-checked against Dates stdlib incl. leap day, pre-epoch and non-leap century; NaN propagation; zero-alloc check. Dates added to test deps (stdlib).
  • Known precision note: timestamps live in Float64 registers, so values exactly on a ms boundary carry ~hundreds of ns of error and may floor to the neighbouring part. Tests use sub-ms offsets deliberately.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants