pgtype/hstore: Save 2 allocs in database/sql Scan implementation
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)
This commit is contained in:
committed by
Jack Christensen
parent
ee04d4a74d
commit
1b68b5970e
+7
-3
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user