lang: Improve graph shape with speculative execution

Most of the time, we don't need to have a dynamic call sub graph, since
the actual function call could be represented statically as it
originally was before lambda functions were implemented. Simplifying the
graph shape has important performance benefits in terms of both keep the
graph smaller (memory, etc) and in avoiding the need to run transactions
at runtime (speed) to reshape the graph.

Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This commit is contained in:
James Shubin
2025-03-17 02:31:01 -04:00
parent 9c9f2f558a
commit 37bb67dffd
139 changed files with 1871 additions and 262 deletions

View File

@@ -508,6 +508,41 @@ func (obj *FuncSingleton) GraphFunc() (*pgraph.Graph, Func, error) {
return obj.g, obj.f, nil
}
// ValueEnv is a future potential argument to the Value() method on Expr.
// XXX: Consider using this if we want to improve graph shape even more.
type ValueEnv struct {
Variables map[Expr]types.Value
}
// EmptyValueEnv returns the zero, empty value for the value env, with all the
// internal maps initialized appropriately.
func EmptyValueEnv() *ValueEnv {
return &ValueEnv{
Variables: make(map[Expr]types.Value),
}
}
// Copy makes a copy of the ValueEnv struct. This ensures that if the internal
// maps are changed, it doesn't affect other copies of the ValueEnv. It does
// *not* copy or change the pointers contained within, since these are
// references, and we need those to be consistently pointing to the same things
// after copying.
func (obj *ValueEnv) Copy() *ValueEnv {
if obj == nil { // allow copying nil envs
return EmptyValueEnv()
}
variables := make(map[Expr]types.Value)
for k, v := range obj.Variables { // copy
variables[k] = v // we don't copy the values!
}
return &ValueEnv{
Variables: variables,
}
}
// Arg represents a name identifier for a func or class argument declaration and
// is sometimes accompanied by a type. This does not satisfy the Expr interface.
type Arg struct {

View File

@@ -44,6 +44,10 @@ import (
// FuncSig is the simple signature that is used throughout our implementations.
type FuncSig = func(context.Context, []types.Value) (types.Value, error)
// GraphSig is the simple signature that is used throughout our implementations.
// TODO: Rename this?
type GraphSig = func(Txn, []Func) (Func, error)
// Compile-time guarantee that *types.FuncValue accepts a func of type FuncSig.
var _ = &types.FuncValue{V: FuncSig(nil)}
@@ -53,7 +57,8 @@ var _ = &types.FuncValue{V: FuncSig(nil)}
type Info struct {
Pure bool // is the function pure? (can it be memoized?)
Memo bool // should the function be memoized? (false if too much output)
Slow bool // is the function slow? (avoid speculative execution)
Fast bool // is the function slow? (avoid speculative execution)
Spec bool // can we speculatively execute it? (true for most)
Sig *types.Type // the signature of the function, must be KindFunc
Err error // is this a valid function, or was it created improperly?
}
@@ -116,6 +121,8 @@ type Func interface {
// It must close the Output chan if it's done sending new values out. It
// must send at least one value, or return an error. It may also return
// an error at anytime if it can't continue.
// XXX: Remove this from here, it should appear as StreamableFunc and
// funcs should implement StreamableFunc or CallableFunc or maybe both.
Stream(context.Context) error
}
@@ -137,6 +144,7 @@ type Func interface {
// the InferableFunc interface as well.
type BuildableFunc interface {
Func // implement everything in Func but add the additional requirements
// XXX: Should this be CopyableFunc instead?
// Build takes the known or unified type signature for this function and
// finalizes this structure so that it is now determined, and ready to
@@ -273,7 +281,7 @@ type FuncData struct {
// special functions that are useful in core.
// TODO: This could be replaced if a func ever needs a SetScope method...
type DataFunc interface {
Func // implement everything in Func but add the additional requirements
CopyableFunc // implement everything, but make it also have Copy
// SetData is used by the language to pass our function some code-level
// context.