Skip to content

Commit

Permalink
Optimize zed.Value.Under (#4739)
Browse files Browse the repository at this point in the history
Move the loop in Value.Under into a separate method, Value.under, so the
compiler will inline Under.

goos: darwin
goarch: arm64
pkg: github.com/brimdata/zed
                        |  before.txt  |              after.txt               |
                        |    sec/op    |    sec/op     vs base                |
ValueUnder/primitive-10   2.0560n ± 2%   0.9328n ± 0%  -54.63% (p=0.000 n=10)
ValueUnder/named-10        27.77n ± 0%    27.96n ± 1%   +0.72% (p=0.000 n=10)
geomean                    7.555n         5.107n       -32.40%
  • Loading branch information
nwt authored Aug 7, 2023
1 parent 8f26982 commit b8e1b18
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 8 deletions.
21 changes: 13 additions & 8 deletions value.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,20 +437,25 @@ func (v *Value) MissingAsNull() *Value {
return v
}

// Under resolves named types and untags unions repeatedly, returning a value
// guaranteed to have neither a named type nor a union type.
func (v *Value) Under() *Value {
typ := v.Type
if _, ok := typ.(*TypeNamed); !ok {
if _, ok := typ.(*TypeUnion); !ok {
// common fast path
return v
}
switch v.Type.(type) {
case *TypeUnion, *TypeNamed:
return v.under()
}
bytes := v.Bytes()
// This is the common case; make sure the compiler can inline it.
return v
}

// under contains logic for Under that the compiler won't inline.
func (v *Value) under() *Value {
typ, bytes := v.Type, v.Bytes()
for {
typ = TypeUnder(typ)
union, ok := typ.(*TypeUnion)
if !ok {
return NewValue(typ, bytes)
return &Value{typ, bytes}
}
typ, bytes = union.Untag(bytes)
}
Expand Down
18 changes: 18 additions & 0 deletions value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ import (
"github.com/stretchr/testify/assert"
)

func BenchmarkValueUnder(b *testing.B) {
b.Run("primitive", func(b *testing.B) {
val := zed.Null
b.ResetTimer()
for i := 0; i < b.N; i++ {
val.Under()
}
})
b.Run("named", func(b *testing.B) {
typ, _ := zed.NewContext().LookupTypeNamed("name", zed.TypeNull)
val := zed.NewValue(typ, nil)
b.ResetTimer()
for i := 0; i < b.N; i++ {
val.Under()
}
})
}

func TestValueValidate(t *testing.T) {
recType := zed.NewTypeRecord(0, []zed.Field{
zed.NewField("f", zed.NewTypeSet(0, zed.TypeString)),
Expand Down

0 comments on commit b8e1b18

Please sign in to comment.