From e9a59202cf80f5abc8fdaf3549ece7bb829d5e21 Mon Sep 17 00:00:00 2001 From: Jesse-Cameron Date: Tue, 20 Sep 2022 16:09:11 +1000 Subject: [PATCH] perf: use native builtin for `foldl` and `foldr` benchmark old ns/op new ns/op delta Benchmark_Builtin_substr-8 17914126 16328579 -8.85% Benchmark_Builtin_reverse-8 360776847 346691957 -3.90% Benchmark_Builtin_parseInt-8 8686867 8314151 -4.29% Benchmark_Builtin_base64Decode-8 22157223 21749268 -1.84% Benchmark_Builtin_base64DecodeBytes-8 281975629 281841745 -0.05% Benchmark_Builtin_base64-8 23824149 23716470 -0.45% Benchmark_Builtin_base64_byte_array-8 141846327 141377054 -0.33% Benchmark_Builtin_manifestJsonEx-8 9317781 9279067 -0.42% Benchmark_Builtin_comparison-8 128783954 128362069 -0.33% Benchmark_Builtin_comparison2-8 2082396660 2029123362 -2.56% Benchmark_Builtin_foldl-8 159908963 31278937 -80.44% --- builtins.go | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/builtins.go b/builtins.go index 8728b4f..450625f 100644 --- a/builtins.go +++ b/builtins.go @@ -396,6 +396,68 @@ func builtinJoin(i *interpreter, sep, arrv value) (value, error) { } } +func builtinFoldl(i *interpreter, funcv, arrv, initv value) (value, error) { + fun, err := i.getFunction(funcv) + if err != nil { + return nil, err + } + var len int + var elements []*cachedThunk + switch arrType := arrv.(type) { + case valueString: + len = arrType.length() + for _, item := range arrType.getRunes() { + elements = append(elements, readyThunk(makeStringFromRunes([]rune{item}))) + } + case *valueArray: + len = arrType.length() + elements = arrType.elements + default: + return nil, i.Error("foldl second parameter should be string or array, got " + arrType.getType().name) + } + + accValue := initv + for counter := 0; counter < len; counter++ { + accValue, err = fun.call(i, args([]*cachedThunk{readyThunk(accValue), elements[counter]}...)) + if err != nil { + return nil, err + } + } + + return accValue, nil +} + +func builtinFoldr(i *interpreter, funcv, arrv, initv value) (value, error) { + fun, err := i.getFunction(funcv) + if err != nil { + return nil, err + } + var len int + var elements []*cachedThunk + switch arrType := arrv.(type) { + case valueString: + len = arrType.length() + for _, item := range arrType.getRunes() { + elements = append(elements, readyThunk(makeStringFromRunes([]rune{item}))) + } + case *valueArray: + len = arrType.length() + elements = arrType.elements + default: + return nil, i.Error("foldr second parameter should be string or array, got " + arrType.getType().name) + } + + accValue := initv + for counter := len - 1; counter >= 0; counter-- { + accValue, err = fun.call(i, args([]*cachedThunk{elements[counter], readyThunk(accValue)}...)) + if err != nil { + return nil, err + } + } + + return accValue, nil +} + func builtinReverse(i *interpreter, arrv value) (value, error) { arr, err := i.getArray(arrv) if err != nil { @@ -1636,6 +1698,8 @@ var funcBuiltins = buildBuiltinMap([]builtin{ &binaryBuiltin{name: "join", function: builtinJoin, params: ast.Identifiers{"sep", "arr"}}, &unaryBuiltin{name: "reverse", function: builtinReverse, params: ast.Identifiers{"arr"}}, &binaryBuiltin{name: "filter", function: builtinFilter, params: ast.Identifiers{"func", "arr"}}, + &ternaryBuiltin{name: "foldl", function: builtinFoldl, params: ast.Identifiers{"func", "arr", "init"}}, + &ternaryBuiltin{name: "foldr", function: builtinFoldr, params: ast.Identifiers{"func", "arr", "init"}}, &binaryBuiltin{name: "range", function: builtinRange, params: ast.Identifiers{"from", "to"}}, &binaryBuiltin{name: "primitiveEquals", function: primitiveEquals, params: ast.Identifiers{"x", "y"}}, &binaryBuiltin{name: "equals", function: builtinEquals, params: ast.Identifiers{"x", "y"}},