Skip to content

Commit

Permalink
pgtype/hstore: Save 2 allocs in database/sql Scan implementation
Browse files Browse the repository at this point in the history
Remove unneeded string to []byte to string conversion, which saves 2
allocs and should make Hstore text scanning slightly faster.

The Hstore.Scan() function takes a string as input, converts it to
[]byte, and calls scanPlanTextAnyToHstoreScanner.Scan(). That
function converts []byte back to string and calls parseHstore. This
refactors scanPlanTextAnyToHstoreScanner.Scan into
scanPlanTextAnyToHstoreScanner.scanString so the database/sql Scan
function can call it directly, bypassing this conversion.

The added Benchmark shows this saves 2 allocs for longer strings, and
saves about 5% CPU overall on my M1 Pro. benchstat output:

goos: darwin
goarch: arm64
pkg: github.com/jackc/pgx/v5/pgtype
              │  orig.txt   │              new.txt               │
              │   sec/op    │   sec/op     vs base               │
HstoreScan-10   1.334µ ± 2%   1.257µ ± 2%  -5.77% (p=0.000 n=10)

              │   orig.txt   │               new.txt               │
              │     B/op     │     B/op      vs base               │
HstoreScan-10   2.094Ki ± 0%   1.969Ki ± 0%  -5.97% (p=0.000 n=10)

              │  orig.txt  │              new.txt              │
              │ allocs/op  │ allocs/op   vs base               │
HstoreScan-10   36.00 ± 0%   34.00 ± 0%  -5.56% (p=0.000 n=10)
  • Loading branch information
evanj authored and jackc committed Jun 7, 2023
1 parent ee04d4a commit 1b68b59
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 3 deletions.
10 changes: 7 additions & 3 deletions pgtype/hstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ func (h *Hstore) Scan(src any) error {

switch src := src.(type) {
case string:
return scanPlanTextAnyToHstoreScanner{}.Scan([]byte(src), h)
return scanPlanTextAnyToHstoreScanner{}.scanString(src, h)
}

return fmt.Errorf("cannot scan %T", src)
Expand Down Expand Up @@ -236,14 +236,18 @@ func (scanPlanBinaryHstoreToHstoreScanner) Scan(src []byte, dst any) error {

type scanPlanTextAnyToHstoreScanner struct{}

func (scanPlanTextAnyToHstoreScanner) Scan(src []byte, dst any) error {
func (s scanPlanTextAnyToHstoreScanner) Scan(src []byte, dst any) error {
scanner := (dst).(HstoreScanner)

if src == nil {
return scanner.ScanHstore(Hstore(nil))
}
return s.scanString(string(src), scanner)
}

keys, values, err := parseHstore(string(src))
// scanString does not return nil hstore values because string cannot be nil.
func (scanPlanTextAnyToHstoreScanner) scanString(src string, scanner HstoreScanner) error {
keys, values, err := parseHstore(src)
if err != nil {
return err
}
Expand Down
19 changes: 19 additions & 0 deletions pgtype/hstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,22 @@ func BenchmarkHstoreEncode(b *testing.B) {
})
}
}

func BenchmarkHstoreScan(b *testing.B) {
strs := []string{
"",
`"a"=>"b"`,
`"a"=>"100", "b"=>"200", "c"=>"300", "d"=>"400", "e"=>"500"`,
}

var h pgtype.Hstore
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for _, str := range strs {
err := h.Scan(str)
if err != nil {
b.Fatal(err)
}
}
}
}

0 comments on commit 1b68b59

Please sign in to comment.