From 114ffdada00edb0b621cdeee290f5689460fcfd0 Mon Sep 17 00:00:00 2001 From: Nicolas Legros Date: Tue, 26 Nov 2024 19:27:31 -0500 Subject: [PATCH] perf: Decrease execution times by 1000% [DT-7499] (#277) * perf: Runs from ~14,000ms to ~1,400ms [DT-7499] * perf: Runs from ~14,000ms to ~7,000ms [DT-7499] * Change the name of the userContext function to make it clear that it will clone the context * perf: Runs from ~14,000ms to ~4,000ms [DT-7499] Change the name of the userContext function to make it clear that it will clone the context perf: Runs from ~14,000ms to ~4,000ms [DT-7499] * revert: Remove timing calls [DT-7499] * Replace the imports --------- Co-authored-by: Jocelyn Giroux --- template/extra_runtime.go | 48 +++++++++++++++++++++++----------- template/extra_runtime_test.go | 6 +++-- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/template/extra_runtime.go b/template/extra_runtime.go index 5c690bb..ed92b08 100644 --- a/template/extra_runtime.go +++ b/template/extra_runtime.go @@ -153,7 +153,7 @@ func (t *Template) addRuntimeFuncs() { "substitute": t.substitute, "templateNames": t.getTemplateNames, "templates": t.Templates, - "userContext": t.userContext, + "userContext": t.cloneUserContext, } t.AddFunctions(funcs, runtimeFunc, FuncOptions{ FuncHelp: runtimeFuncsHelp, @@ -167,7 +167,7 @@ func exit(exitValue int) int { os.Exit(exitValue); return exitValue } func (t *Template) current() string { return t.folder } -func (t *Template) userContext() interface{} { +func (t *Template) cloneUserContext() interface{} { return t.Context().Clone().Flush(t.constantKeys...) } @@ -430,19 +430,27 @@ func (t *Template) exec(command string, args ...interface{}) (interface{}, error } func (t *Template) runTemplate(source string, args ...interface{}) (result, filename string, err error) { + return optimizedRunTemplate(t, false, source, args...) +} + +func optimizedRunTemplate(t *Template, withClone bool, source string, args ...interface{}) (result, filename string, err error) { if source == "" { return } var out bytes.Buffer - // Keep the parent context to make it available - parentContext := t.userContext() - // Clone the current context to ensure that the sub template has a distinct set of values - t = t.GetNewContext("", false) - context := t.Context().Clone() - if context.Len() == 0 { - context.Set("CONTEXT", context) + var context collections.IDictionary + + if withClone { + context = t.Context().Clone() + if context.Len() == 0 { + context.Set("CONTEXT", context) + } + context.Set("_", t.cloneUserContext()) + } else { + context = collections.CreateDictionary() } + switch len(args) { case 1: if arguments, err := collections.TryAsDictionary(args[0]); err == nil { @@ -454,10 +462,6 @@ func (t *Template) runTemplate(source string, args ...interface{}) (result, file context.Set("ARGS", args) } - // Make the parent context available - context.Set("_", parentContext) - t.context = context - // We first try to find a template named internalTemplate := t.Lookup(source) if internalTemplate == nil { @@ -490,8 +494,21 @@ func (t *Template) runTemplate(source string, args ...interface{}) (result, file } // We execute the resulting template - if err = internalTemplate.Execute(&out, t.context); err != nil { - return + if withClone { + internalTemplate.Option("missingkey=default") + } else { + internalTemplate.Option("missingkey=error") + } + + previous_context := t.context + t.context = context + err = internalTemplate.Execute(&out, context) + t.context = previous_context + if err != nil { + if !withClone { + TemplateLog.Debug("Running template with context cloning because:", err) + return optimizedRunTemplate(t, true, source, args...) + } } result = out.String() @@ -511,6 +528,7 @@ func (t *Template) runTemplate(source string, args ...interface{}) (result, file filename = "" } return + } func (t *Template) runTemplateItf(source string, context ...interface{}) (interface{}, error) { diff --git a/template/extra_runtime_test.go b/template/extra_runtime_test.go index 0c1ee96..66f61ef 100644 --- a/template/extra_runtime_test.go +++ b/template/extra_runtime_test.go @@ -36,10 +36,12 @@ func TestRuntime(t *testing.T) { { name: "Get context", content: `@define("func") + @base = 2 @-context() @-end - @-include("func", 1, 2, 3)`, - result: `{"ARGS":[1,2,3],"_":{"base":1},"base":1}`, + @-include("func", 1, 2, 3) + @-context()`, + result: `{"ARGS":[1,2,3],"_":{"base":1},"base":2}{"base":1}`, }, { name: "Override parent value",