Skip to content

Commit

Permalink
optimize performace when setting multiple variables at once (closes #4)
Browse files Browse the repository at this point in the history
  • Loading branch information
xrstf committed Dec 21, 2023
1 parent d29a620 commit e84576d
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 17 deletions.
58 changes: 44 additions & 14 deletions pkg/builtin/lists/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,17 @@ func rangeVectorFunction(ctx types.Context, data []any, namingVec ast.Expression
)

for i, item := range data {
// do not use separate contexts for each loop iteration, as the loop might build up a counter
loopCtx = loopCtx.WithVariable(loopVarName, item)
vars := map[string]any{
loopVarName: item,
}

if loopIndexName != "" {
loopCtx = loopCtx.WithVariable(loopIndexName, i)
vars[loopIndexName] = i
}

// do not use separate contexts for each loop iteration, as the loop might build up a counter
loopCtx = loopCtx.WithVariables(vars)

loopCtx, result, err = eval.EvalExpression(loopCtx, expr)
if err != nil {
return nil, err
Expand All @@ -98,12 +103,17 @@ func rangeObjectFunction(ctx types.Context, data map[string]any, namingVec ast.E
)

for key, value := range data {
// do not use separate contexts for each loop iteration, as the loop might build up a counter
loopCtx = loopCtx.WithVariable(loopVarName, value)
vars := map[string]any{
loopVarName: value,
}

if loopIndexName != "" {
loopCtx = loopCtx.WithVariable(loopIndexName, key)
vars[loopIndexName] = key
}

// do not use separate contexts for each loop iteration, as the loop might build up a counter
loopCtx = loopCtx.WithVariables(vars)

loopCtx, result, err = eval.EvalExpression(loopCtx, expr)
if err != nil {
return nil, err
Expand Down Expand Up @@ -140,11 +150,16 @@ func mapVectorExpressionFunction(ctx types.Context, data []any, namingVec ast.Ex
}

mapHandler := func(ctx types.Context, index any, value any) (types.Context, any, error) {
ctx = ctx.WithVariable(valueVarName, value)
vars := map[string]any{
valueVarName: value,
}

if indexVarName != "" {
ctx = ctx.WithVariable(indexVarName, index)
vars[indexVarName] = index
}

ctx = ctx.WithVariables(vars)

return eval.EvalExpression(ctx, expr)
}

Expand Down Expand Up @@ -197,11 +212,16 @@ func mapObjectExpressionFunction(ctx types.Context, data map[string]any, namingV
}

mapHandler := func(ctx types.Context, key any, value any) (types.Context, any, error) {
ctx = ctx.WithVariable(valueVarName, value)
vars := map[string]any{
valueVarName: value,
}

if keyVarName != "" {
ctx = ctx.WithVariable(keyVarName, key)
vars[keyVarName] = key
}

ctx = ctx.WithVariables(vars)

return eval.EvalExpression(ctx, expr)
}

Expand Down Expand Up @@ -254,11 +274,16 @@ func filterVectorExpressionFunction(ctx types.Context, data []any, namingVec ast
}

mapHandler := func(ctx types.Context, index any, value any) (types.Context, any, error) {
ctx = ctx.WithVariable(valueVarName, value)
vars := map[string]any{
valueVarName: value,
}

if indexVarName != "" {
ctx = ctx.WithVariable(indexVarName, index)
vars[indexVarName] = index
}

ctx = ctx.WithVariables(vars)

return eval.EvalExpression(ctx, expr)
}

Expand Down Expand Up @@ -317,11 +342,16 @@ func filterObjectExpressionFunction(ctx types.Context, data map[string]any, nami
}

mapHandler := func(ctx types.Context, key any, value any) (types.Context, any, error) {
ctx = ctx.WithVariable(valueVarName, value)
vars := map[string]any{
valueVarName: value,
}

if keyVarName != "" {
ctx = ctx.WithVariable(keyVarName, key)
vars[keyVarName] = key
}

ctx = ctx.WithVariables(vars)

return eval.EvalExpression(ctx, expr)
}

Expand Down
6 changes: 3 additions & 3 deletions pkg/builtin/rudifunc/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ func (f rudispaceFunc) Evaluate(ctx types.Context, args []ast.Expression) (any,
return nil, fmt.Errorf("expected %d argument(s), got %d", len(f.params), len(args))
}

funcCtx := ctx
funcArgs := map[string]any{}
for i, paramName := range f.params {
_, arg, err := eval.EvalExpression(ctx, args[i])
if err != nil {
return nil, err
}

funcCtx = funcCtx.WithVariable(paramName, arg)
funcArgs[paramName] = arg
}

_, result, err := eval.EvalExpression(funcCtx, f.body)
_, result, err := eval.EvalExpression(ctx.WithVariables(funcArgs), f.body)

return result, err
}
29 changes: 29 additions & 0 deletions pkg/eval/types/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,21 @@ func (c Context) WithVariable(name string, val any) Context {
}
}

func (c Context) WithVariables(vars map[string]any) Context {
if len(vars) == 0 {
return c
}

return Context{
ctx: c.ctx,
document: c.document,
fixedFuncs: c.fixedFuncs,
userFuncs: c.userFuncs,
variables: c.variables.WithMany(vars),
coalescer: c.coalescer,
}
}

func (c Context) WithCoalescer(coalescer coalescing.Coalescer) Context {
return Context{
ctx: c.ctx,
Expand Down Expand Up @@ -246,6 +261,20 @@ func (v Variables) With(name string, val any) Variables {
return v.DeepCopy().Set(name, val)
}

// WithMany is like With(), but for adding multiple new variables at once. This
// should be preferred to With() to prevent unnecessary DeepCopies.
func (v Variables) WithMany(vars map[string]any) Variables {
if len(vars) == 0 {
return v
}

out := v.DeepCopy()
for k, v := range vars {
out.Set(k, v)
}
return out
}

func (v Variables) DeepCopy() Variables {
result := NewVariables()
for key, val := range v {
Expand Down

0 comments on commit e84576d

Please sign in to comment.