feat: improve stdlib base64 performance by 98.58%+

Implements std.base64 in native Go, improving performance

benchmark                                 old ns/op       new ns/op     delta
Benchmark_Builtin_base64-16               10805730974     23158636      -99.79%
Benchmark_Builtin_base64_bytearray-16     8682808704      123360964     -98.58%
This commit is contained in:
Wes McNamee 2019-12-15 09:19:13 -08:00 committed by Stanisław Barzowski
parent 492503d13b
commit e8bd3f4ff8
18 changed files with 142 additions and 0 deletions

View File

@ -0,0 +1,5 @@
{
foo: [
std.base64("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed turpis tincidunt id aliquet risus. Eget mauris pharetra et ultrices neque ornare aenean euismod. Diam quis enim lobortis scelerisque fermentum. Varius duis at consectetur lorem donec massa sapien. Diam sit amet nisl suscipit adipiscing bibendum est ultricies integer. Lectus urna duis convallis convallis tellus. Nibh ipsum consequat nisl vel pretium lectus quam id leo. Feugiat in ante metus dictum at tempor commodo. Velit dignissim sodales ut eu sem integer. Dictum sit amet justo donec. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed turpis tincidunt id aliquet risus. Eget mauris pharetra et ultrices neque ornare aenean euismod. Diam quis enim lobortis scelerisque fermentum. Varius duis at consectetur lorem donec massa sapien. Diam sit amet nisl suscipit adipiscing bibendum est ultricies integer. Lectus urna duis convallis convallis tellus. Nibh ipsum consequat nisl vel pretium lectus quam id leo. Feugiat in ante metus dictum at tempor commodo. Velit dignissim sodales ut eu sem integer. Dictum sit amet justo donec. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed turpis tincidunt id aliquet risus. Eget mauris pharetra et ultrices neque ornare aenean euismod. Diam quis enim lobortis scelerisque fermentum. Varius duis at consectetur lorem donec massa sapien. Diam sit amet nisl suscipit adipiscing bibendum est ultricies integer. Lectus urna duis convallis convallis tellus. Nibh ipsum consequat nisl vel pretium lectus quam id leo. Feugiat in ante metus dictum at tempor commodo. Velit dignissim sodales ut eu sem integer. Dictum sit amet justo donec. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus.Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed turpis tincidunt id aliquet risus. Eget mauris pharetra et ultrices neque ornare aenean euismod. Diam quis enim lobortis scelerisque fermentum. Varius duis at consectetur lorem donec massa sapien. Diam sit amet nisl suscipit adipiscing bibendum est ultricies integer. Lectus urna duis convallis convallis tellus. Nibh ipsum consequat nisl vel pretium lectus quam id leo. Feugiat in ante metus dictum at tempor commodo. Velit dignissim sodales ut eu sem integer. Dictum sit amet justo donec. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed turpis tincidunt id aliquet risus. Eget mauris pharetra et ultrices neque ornare aenean euismod. Diam quis enim lobortis scelerisque fermentum. Varius duis at consectetur lorem donec massa sapien. Diam sit amet nisl suscipit adipiscing bibendum est ultricies integer. Lectus urna duis convallis convallis tellus. Nibh ipsum consequat nisl vel pretium lectus quam id leo. Feugiat in ante metus dictum at tempor commodo. Velit dignissim sodales ut eu sem integer. Dictum sit amet justo donec. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus. Scelerisque mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus.") for i in std.range(0,100)
],
}

File diff suppressed because one or more lines are too long

View File

@ -19,6 +19,7 @@ package jsonnet
import ( import (
"bytes" "bytes"
"crypto/md5" "crypto/md5"
"encoding/base64"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -681,6 +682,69 @@ func builtinMd5(i *interpreter, trace traceElement, x value) (value, error) {
return makeValueString(hex.EncodeToString(hash[:])), nil return makeValueString(hex.EncodeToString(hash[:])), nil
} }
func builtinBase64(i *interpreter, trace traceElement, input value) (value, error) {
var byteArr []byte
var sanityCheck = func(v int) (string, bool) {
if v < 0 || 255 < v {
msg := fmt.Sprintf("base64 encountered invalid codepoint value in the array (must be 0 <= X <= 255), got %d", v)
return msg, false
}
return "", true
}
switch input.(type) {
case valueString:
vStr, err := i.getString(input, trace)
if err != nil {
return nil, err
}
runes := []rune(vStr.getGoString())
for _, r := range runes {
n := int(r)
msg, ok := sanityCheck(n)
if !ok {
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
}
}
byteArr = []byte(string(vStr.getGoString()))
case *valueArray:
vArr, err := i.getArray(input, trace)
if err != nil {
return nil, err
}
for _, cThunk := range vArr.elements {
cTv, err := cThunk.getValue(i, trace)
if err != nil {
return nil, err
}
vInt, err := i.getInt(cTv, trace)
if err != nil {
msg := fmt.Sprintf("base64 encountered a non-integer value in the array, got %s", cTv.getType().name)
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
}
msg, ok := sanityCheck(vInt)
if !ok {
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
}
byteArr = append(byteArr, byte(vInt))
}
default:
msg := fmt.Sprintf("base64 can only base64 encode strings / arrays of single bytes, got %s", input.getType().name)
return nil, makeRuntimeError(msg, i.getCurrentStackTrace(trace))
}
sEnc := base64.StdEncoding.EncodeToString(byteArr)
return makeValueString(sEnc), nil
}
func builtinEncodeUTF8(i *interpreter, trace traceElement, x value) (value, error) { func builtinEncodeUTF8(i *interpreter, trace traceElement, x value) (value, error) {
str, err := i.getString(x, trace) str, err := i.getString(x, trace)
if err != nil { if err != nil {
@ -1318,6 +1382,7 @@ var funcBuiltins = buildBuiltinMap([]builtin{
&ternaryBuiltin{name: "splitLimit", function: builtinSplitLimit, parameters: ast.Identifiers{"str", "c", "maxsplits"}}, &ternaryBuiltin{name: "splitLimit", function: builtinSplitLimit, parameters: ast.Identifiers{"str", "c", "maxsplits"}},
&ternaryBuiltin{name: "strReplace", function: builtinStrReplace, parameters: ast.Identifiers{"str", "from", "to"}}, &ternaryBuiltin{name: "strReplace", function: builtinStrReplace, parameters: ast.Identifiers{"str", "from", "to"}},
&unaryBuiltin{name: "parseJson", function: builtinParseJSON, parameters: ast.Identifiers{"str"}}, &unaryBuiltin{name: "parseJson", function: builtinParseJSON, parameters: ast.Identifiers{"str"}},
&unaryBuiltin{name: "base64", function: builtinBase64, parameters: ast.Identifiers{"input"}},
&unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, parameters: ast.Identifiers{"str"}}, &unaryBuiltin{name: "encodeUTF8", function: builtinEncodeUTF8, parameters: ast.Identifiers{"str"}},
&unaryBuiltin{name: "decodeUTF8", function: builtinDecodeUTF8, parameters: ast.Identifiers{"arr"}}, &unaryBuiltin{name: "decodeUTF8", function: builtinDecodeUTF8, parameters: ast.Identifiers{"arr"}},
&generalBuiltin{name: "sort", function: builtinSort, required: ast.Identifiers{"arr"}, optional: ast.Identifiers{"keyF"}, defaultValues: []value{functionID}}, &generalBuiltin{name: "sort", function: builtinSort, required: ast.Identifiers{"arr"}, optional: ast.Identifiers{"keyF"}, defaultValues: []value{functionID}},

View File

@ -38,3 +38,11 @@ func Benchmark_Builtin_substr(b *testing.B) {
func Benchmark_Builtin_reverse(b *testing.B) { func Benchmark_Builtin_reverse(b *testing.B) {
RunBenchmark(b, "reverse") RunBenchmark(b, "reverse")
} }
func Benchmark_Builtin_base64(b *testing.B) {
RunBenchmark(b, "base64")
}
func Benchmark_Builtin_base64_byte_array(b *testing.B) {
RunBenchmark(b, "base64_byte_array")
}

1
testdata/builtinBase64.golden vendored Normal file
View File

@ -0,0 +1 @@
"aGVsbG8="

1
testdata/builtinBase64.jsonnet vendored Normal file
View File

@ -0,0 +1 @@
std.base64("hello")

View File

@ -0,0 +1 @@
"aGVsbG8="

View File

@ -0,0 +1 @@
std.base64([104, 101, 108, 108, 111])

View File

@ -0,0 +1,10 @@
RUNTIME ERROR: base64 encountered a non-integer value in the array, got string
-------------------------------------------------
testdata/builtinBase64_invalid_byte_array:1:1-23 builtin function <base64>
std.base64([1, "foo"])
-------------------------------------------------
During evaluation

View File

@ -0,0 +1 @@
std.base64([1, "foo"])

View File

@ -0,0 +1,10 @@
RUNTIME ERROR: base64 encountered invalid codepoint value in the array (must be 0 <= X <= 255), got -1
-------------------------------------------------
testdata/builtinBase64_invalid_byte_array1:1:1-20 builtin function <base64>
std.base64([1, -1])
-------------------------------------------------
During evaluation

View File

@ -0,0 +1 @@
std.base64([1, -1])

View File

@ -0,0 +1,10 @@
RUNTIME ERROR: base64 encountered invalid codepoint value in the array (must be 0 <= X <= 255), got 256
-------------------------------------------------
testdata/builtinBase64_invalid_byte_array2:1:1-21 builtin function <base64>
std.base64([1, 256])
-------------------------------------------------
During evaluation

View File

@ -0,0 +1 @@
std.base64([1, 256])

View File

@ -0,0 +1,10 @@
RUNTIME ERROR: base64 can only base64 encode strings / arrays of single bytes, got number
-------------------------------------------------
testdata/builtinBase64_non_string_non_array:1:1-14 builtin function <base64>
std.base64(1)
-------------------------------------------------
During evaluation

View File

@ -0,0 +1 @@
std.base64(1)

View File

@ -0,0 +1,10 @@
RUNTIME ERROR: base64 encountered invalid codepoint value in the array (must be 0 <= X <= 255), got 256
-------------------------------------------------
testdata/builtinBase64_string_high_codepoint:1:1-17 builtin function <base64>
std.base64("Ā")
-------------------------------------------------
During evaluation

View File

@ -0,0 +1 @@
std.base64("Ā")