Skip to content

julia-vscode/Salsa.jl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

213 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Salsa.jl

Build Status

A framework for on-demand, incremental computation via memoization, inspired by Rust lang's salsa-rs/salsa.

⏯ Youtube | JuliaCon 2020 | Salsa.jl

Description

Salsa is:

  • a memoization framework, with
  • runtime dependency tracking, so that
  • you can update some inputs and (performantly) automatically invalidate the affected caches.

It provides a framework for automating away the potential pitfalls of cache invalidation, by automatically detecting dependencies between parts of your code (@derived functions), and using the detected dependency graph to propagate invalidations when facts about the world have changed.

Usage

  • @derived
  • @declare_input
  • Runtime()
julia> using Salsa

julia> @declare_input x(rt)::Int
(x, set_x!, delete_x!)

julia> @derived function x_plus_one(rt)
           println("Running x_plus_one.")
           return x(rt) + 1
       end
x_plus_one (generic function with 1 method)
julia> rt = Salsa.Runtime();

julia> set_x!(rt, 1)

julia> x_plus_one(rt)
Running x_plus_one.
2

julia> x_plus_one(rt)
2

julia> set_x!(rt, 10)

julia> x_plus_one(rt)
Running x_plus_one.
11

Lazy Inputs

By default, accessing an input that hasn't been set throws an error. With lazy inputs, you can provide a callback function that computes the value on-demand the first time it is accessed. This is useful for inputs backed by external data sources (files, databases, etc.) where you want values to be loaded only when needed.

julia> function load_student_grade(ctx, name::String)
           println("Loading grade for $name...")
           return name == "Alice" ? 3.5 : 2.0
       end
load_student_grade (generic function with 1 method)

julia> @declare_input student_grade(rt, name::String)::Float64 load_student_grade
(student_grade, set_student_grade!, delete_student_grade!)

julia> @derived function pass_fail(rt, name::String)
           student_grade(rt, name) >= 3.0 ? "Pass" : "Fail"
       end
pass_fail (generic function with 1 method)
julia> rt = Salsa.Runtime();

julia> pass_fail(rt, "Alice")
Loading grade for Alice...
"Pass"

julia> pass_fail(rt, "Alice")
"Pass"

The callback signature is callback(context, args...) where context is the Runtime's context object and args... match the input's key arguments. The callback is guaranteed to be called at most once per key, even under concurrent access from multiple threads — other threads requesting the same key will wait for the first computation to complete.

You can override a lazy-computed value with set_input!, and you can delete it with the generated delete_*! function to force recomputation on next access.

Flags

For maximum performance in deployed software, you can disable all runtime assertions and debug code by setting this environment variable before building Salsa: SALSA_STATIC_DEBUG=false.

Or, for a slightly smaller performance gain, you can toggle it at runtime via Salsa.Debug.disable_debug().

Credits

This package was closely modeled on the Rust salsa framework, and takes heavy inspiration from that framework and adapton.

We highly recommend this talk which motivates the need for incremental, demand-driven computation, and for packages like Salsa:

Comparison with the Rust Salsa-rs framework

The underlying principles are very similar to, and inspired from that package: It can be hard to write correct incremental programs by hand, so we provide macros that make it easy by automatically tracking dependencies between computations.

If you are familiar with Salsa-rs, you'll see many things that are similar, with slightly more generic names that are moved away from database-oriented naming:

  • derived queries => @derived functions
  • query group => Runtime

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages