Skip to content

Commit

Permalink
types/basetypes: Reduce memory allocations of (ObjectValue).ToTerrafo…
Browse files Browse the repository at this point in the history
…rmValue() (#791)

Reference: #775

The prior implementation used the exported `AttributeTypes()` method, which in this case was unnecessarily creating a copy of the map when it was only be used for walking the existing data. At scale, such as handling 1000+ objects, this creates unnecessary pressure on the Go garbage collector.

`benchstat` comparison with new benchmark test:

```text
goos: darwin
goarch: arm64
pkg: github.com/hashicorp/terraform-plugin-framework/types/basetypes
                                   │  original   │              proposed               │
                                   │   sec/op    │   sec/op     vs base                │
ObjectValueToTerraformValue1000-10   408.4µ ± 1%   318.4µ ± 1%  -22.02% (p=0.000 n=10)

                                   │   original   │               proposed               │
                                   │     B/op     │     B/op      vs base                │
ObjectValueToTerraformValue1000-10   449.2Ki ± 0%   286.6Ki ± 0%  -36.19% (p=0.000 n=10)

                                   │  original   │              proposed              │
                                   │  allocs/op  │  allocs/op   vs base               │
ObjectValueToTerraformValue1000-10   2.051k ± 0%   2.020k ± 0%  -1.51% (p=0.000 n=10)
```
  • Loading branch information
bflad authored Jul 5, 2023
1 parent fd308f1 commit 9fe2ca5
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/BUG FIXES-20230703-123518.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: BUG FIXES
body: 'types/basetypes: Minor reduction of memory allocations for `ObjectValue` type
`ToTerraformValue()` method, which decreases provider operation durations at scale'
time: 2023-07-03T12:35:18.484275-04:00
custom:
Issue: "775"
6 changes: 4 additions & 2 deletions types/basetypes/object_value.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,12 @@ func (o ObjectValue) Type(ctx context.Context) attr.Type {
// ToTerraformValue returns the data contained in the attr.Value as
// a tftypes.Value.
func (o ObjectValue) ToTerraformValue(ctx context.Context) (tftypes.Value, error) {
attrTypes := map[string]tftypes.Type{}
for attr, typ := range o.AttributeTypes(ctx) {
attrTypes := make(map[string]tftypes.Type, len(o.attributeTypes))

for attr, typ := range o.attributeTypes {
attrTypes[attr] = typ.TerraformType(ctx)
}

objectType := tftypes.Object{AttributeTypes: attrTypes}

switch o.state {
Expand Down
30 changes: 30 additions & 0 deletions types/basetypes/object_value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package basetypes
import (
"context"
"math/big"
"strconv"
"testing"

"github.com/google/go-cmp/cmp"
Expand All @@ -15,6 +16,35 @@ import (
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

func BenchmarkObjectValueToTerraformValue1000(b *testing.B) {
benchmarkObjectValueToTerraformValue(b, 1000)
}

func benchmarkObjectValueToTerraformValue(b *testing.B, attributes int) {
attributeTypes := make(map[string]attr.Type, attributes)
attributeValues := make(map[string]attr.Value, attributes)
ctx := context.Background()

for i := 0; i < attributes; i++ {
attributeName := "testattr" + strconv.Itoa(i)
attributeTypes[attributeName] = BoolType{}
attributeValues[attributeName] = NewBoolNull()
}

value := NewObjectValueMust(
attributeTypes,
attributeValues,
)

for n := 0; n < b.N; n++ {
_, err := value.ToTerraformValue(ctx)

if err != nil {
b.Fatalf("unexpected ToTerraformValue error: %s", err)
}
}
}

func TestNewObjectValue(t *testing.T) {
t.Parallel()

Expand Down

0 comments on commit 9fe2ca5

Please sign in to comment.