Skip to content

Commit

Permalink
typedesc: avoid slice copy when hydrating enums
Browse files Browse the repository at this point in the history
A profile showed that when an enum has a lot of values, hydrating it can
become expensive since it requires initializing a large slice.

Since these objects are immutable, we can just reference the slice
directly.

```
goos: darwin
goarch: arm64
                    │   bench-old   │              bench-new              │
                    │    sec/op     │    sec/op     vs base               │
ResolveTypeByOID-10   106.266µ ± 4%   1.246µ ± 14%  -98.83% (p=0.002 n=6)

                    │   bench-old   │             bench-new             │
                    │     B/op      │    B/op     vs base               │
ResolveTypeByOID-10   422992.5 ± 0%   453.0 ± 0%  -99.89% (p=0.002 n=6)

                    │ bench-old  │             bench-new             │
                    │ allocs/op  │ allocs/op   vs base               │
ResolveTypeByOID-10   51.00 ± 4%   12.00 ± 0%  -76.47% (p=0.002 n=6)
```

Release note (performance improvement): Improved the cost of resolving a
user-defined enum type that has many values.
  • Loading branch information
rafiss committed Aug 28, 2023
1 parent 0869917 commit 38d44a0
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 12 deletions.
30 changes: 20 additions & 10 deletions pkg/sql/catalog/typedesc/hydrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,26 @@ func ensureTypeMetadataIsHydrated(
return
}
if e := maybeDesc.AsEnumTypeDescriptor(); e != nil {
n := e.NumEnumMembers()
tm.EnumData = &types.EnumMetadata{
LogicalRepresentations: make([]string, n),
PhysicalRepresentations: make([][]byte, n),
IsMemberReadOnly: make([]bool, n),
}
for i := 0; i < n; i++ {
tm.EnumData.LogicalRepresentations[i] = e.GetMemberLogicalRepresentation(i)
tm.EnumData.PhysicalRepresentations[i] = e.GetMemberPhysicalRepresentation(i)
tm.EnumData.IsMemberReadOnly[i] = e.IsMemberReadOnly(i)
if imm, ok := e.(*immutable); ok {
// Fast-path for immutable enum descriptors. We can use a pointer into the
// immutable descriptor's slices, since the TypMetadata is immutable too.
tm.EnumData = &types.EnumMetadata{
LogicalRepresentations: imm.logicalReps,
PhysicalRepresentations: imm.physicalReps,
IsMemberReadOnly: imm.readOnlyMembers,
}
} else {
n := e.NumEnumMembers()
tm.EnumData = &types.EnumMetadata{
LogicalRepresentations: make([]string, n),
PhysicalRepresentations: make([][]byte, n),
IsMemberReadOnly: make([]bool, n),
}
for i := 0; i < n; i++ {
tm.EnumData.LogicalRepresentations[i] = e.GetMemberLogicalRepresentation(i)
tm.EnumData.PhysicalRepresentations[i] = e.GetMemberPhysicalRepresentation(i)
tm.EnumData.IsMemberReadOnly[i] = e.IsMemberReadOnly(i)
}
}
}
}
4 changes: 2 additions & 2 deletions pkg/sql/schema_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ func BenchmarkResolveTypeByOID(b *testing.B) {

ctx := context.Background()
execCfg := s.ExecutorConfig().(ExecutorConfig)
sd := NewInternalSessionData(ctx, execCfg.Settings, "test")
sd := NewFakeSessionData(&execCfg.Settings.SV, "test")
sd.Database = "defaultdb"
planner, cleanup := newInternalPlanner("test", kv.NewTxn(ctx, kvDB, s.NodeID()),
username.RootUserName(), &MemoryMetrics{}, &execCfg, sd,
username.RootUserName(), &MemoryMetrics{}, &execCfg, sd.SessionData,
)
defer cleanup()

Expand Down

0 comments on commit 38d44a0

Please sign in to comment.