Skip to content

Commit

Permalink
Merge #32805 #32866
Browse files Browse the repository at this point in the history
32805: parser: batch some allocations in the scanner r=RaduBerinde a=RaduBerinde

The scanner allocates copies of all uppercase identifiers; preallocate
a buffer (proportional to the size of the query) to save some of these
allocations.

Benchmark:
```
name   old time/op    new time/op    delta
Parse    33.8µs ± 1%    33.7µs ± 1%     ~     (p=0.699 n=6+6)

name   old alloc/op   new alloc/op   delta
Parse    5.54kB ± 0%    5.68kB ± 0%   +2.60%  (p=0.002 n=6+6)

name   old allocs/op  new allocs/op  delta
Parse      92.0 ± 0%      81.0 ± 0%  -11.96%  (p=0.002 n=6+6)
```

Release note: None

32866: update cloud storage deps r=RaduBerinde a=RaduBerinde

Informs #30774.

Release note: None

googleapis/google-cloud-go@0fd7230...0ebda48
Azure/azure-pipeline-go@7571e8e...b8e3409
aws/aws-sdk-go@ee1f179...ddc06f9

I ran all the ccl tests with `customenv.mk`. The Azure keys are no longer valid, all other tests passed.

Co-authored-by: Radu Berinde <radu@cockroachlabs.com>
  • Loading branch information
craig[bot] and RaduBerinde committed Dec 5, 2018
3 parents cf9c175 + de87c78 + 86a673f commit 91b11da
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 42 deletions.
32 changes: 16 additions & 16 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pkg/ccl/storageccl/export_storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ func (g *gcsStorage) Size(ctx context.Context, basename string) (int64, error) {
if err != nil {
return 0, err
}
sz := r.Size()
sz := r.Attrs.Size
_ = r.Close()
return sz, nil
}
Expand Down
49 changes: 33 additions & 16 deletions pkg/sql/parser/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2785,21 +2785,38 @@ func TestUnimplementedSyntax(t *testing.T) {
}

func BenchmarkParse(b *testing.B) {
for i := 0; i < b.N; i++ {
st, err := parser.Parse(`
BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + 77 WHERE aid = 5;
SELECT abalance FROM pgbench_accounts WHERE aid = 5;
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (1, 2, 5, 77, CURRENT_TIMESTAMP);
END`)
if err != nil {
b.Fatal(err)
}
if len(st) != 5 {
b.Fatal("parsed wrong number of statements: ", len(st))
}
if _, ok := st[1].(*tree.Update); !ok {
b.Fatalf("unexpected statement type: %T", st[1])
}
testCases := []struct {
name, query string
}{
{
"simple",
`SELECT a FROM t WHERE a = 1`,
},
{
"string",
`SELECT a FROM t WHERE a = 'some-string' AND b = 'some-other-string'`,
},
{
"tpcc-delivery",
`SELECT no_o_id FROM new_order WHERE no_w_id = $1 AND no_d_id = $2 ORDER BY no_o_id ASC LIMIT 1`,
},
{
"account",
`BEGIN;
UPDATE pgbench_accounts SET abalance = abalance + 77 WHERE aid = 5;
SELECT abalance FROM pgbench_accounts WHERE aid = 5;
INSERT INTO pgbench_history (tid, bid, aid, delta, mtime) VALUES (1, 2, 5, 77, CURRENT_TIMESTAMP);
END`,
},
}
for _, tc := range testCases {
b.Run(tc.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := parser.Parse(tc.query)
if err != nil {
b.Fatal(err)
}
}
})
}
}
54 changes: 46 additions & 8 deletions pkg/sql/parser/scan.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ type Scanner struct {
// stmts contains the list of statements at the end of parsing.
stmts []tree.Statement

initialized bool
initialized bool
bytesPrealloc []byte
}

// scanErr holds error state for a Scanner.
Expand All @@ -72,6 +73,41 @@ func (s *Scanner) init(str string) {
}
s.initialized = true
s.in = str
// Preallocate some buffer space for identifiers etc.
s.bytesPrealloc = make([]byte, len(str))
}

func (s *Scanner) allocBytes(length int) []byte {
if len(s.bytesPrealloc) >= length {
res := s.bytesPrealloc[:length:length]
s.bytesPrealloc = s.bytesPrealloc[length:]
return res
}
return make([]byte, length)
}

// buffer returns an empty []byte buffer that can be appended to. Any unused
// portion can be returned later using returnBuffer.
func (s *Scanner) buffer() []byte {
buf := s.bytesPrealloc[:0]
s.bytesPrealloc = nil
return buf
}

// returnBuffer returns the unused portion of buf to the scanner, to be used for
// future allocBytes() or buffer() calls. The caller must not use buf again.
func (s *Scanner) returnBuffer(buf []byte) {
if len(buf) < cap(buf) {
s.bytesPrealloc = buf[len(buf):]
}
}

// finishString casts the given buffer to a string and returns the unused
// portion of the buffer. The caller must not use buf again.
func (s *Scanner) finishString(buf []byte) string {
str := *(*string)(unsafe.Pointer(&buf))
s.returnBuffer(buf)
return str
}

// Tokens calls f on all tokens of the input until an EOF is encountered.
Expand Down Expand Up @@ -671,7 +707,8 @@ func (s *Scanner) scanIdent(lval *sqlSymType) {
} else if isASCII {
// We know that the identifier we've seen so far is ASCII, so we don't need
// to unicode normalize. Instead, just lowercase as normal.
b := make([]byte, s.pos-start)
b := s.allocBytes(s.pos - start)
_ = b[s.pos-start-1] // For bounds check elimination.
for i, c := range s.in[start:s.pos] {
if c >= 'A' && c <= 'Z' {
c += 'a' - 'A'
Expand Down Expand Up @@ -810,7 +847,8 @@ func (s *Scanner) scanPlaceholder(lval *sqlSymType) {

// scanHexString scans the content inside x'....'.
func (s *Scanner) scanHexString(lval *sqlSymType, ch int) bool {
var buf []byte
buf := s.buffer()

var curbyte byte
bytep := 0
const errInvalidBytesLiteral = "invalid hexadecimal bytes literal"
Expand Down Expand Up @@ -860,13 +898,13 @@ outer:
}

lval.id = BCONST
lval.str = *(*string)(unsafe.Pointer(&buf))
lval.str = s.finishString(buf)
return true
}

// scanBitString scans the content inside B'....'.
func (s *Scanner) scanBitString(lval *sqlSymType, ch int) bool {
var buf []byte
buf := s.buffer()
outer:
for {
b := s.next()
Expand Down Expand Up @@ -896,15 +934,15 @@ outer:
}

lval.id = BITCONST
lval.str = *(*string)(unsafe.Pointer(&buf))
lval.str = s.finishString(buf)
return true
}

// scanString scans the content inside '...'. This is used for simple
// string literals '...' but also e'....' and b'...'. For x'...', see
// scanHexString().
func (s *Scanner) scanString(lval *sqlSymType, ch int, allowEscapes, requireUTF8 bool) bool {
var buf []byte
buf := s.buffer()
var runeTmp [utf8.UTFMax]byte
start := s.pos

Expand Down Expand Up @@ -995,6 +1033,6 @@ outer:
return false
}

lval.str = *(*string)(unsafe.Pointer(&buf))
lval.str = s.finishString(buf)
return true
}
2 changes: 1 addition & 1 deletion vendor
Submodule vendor updated 150 files

0 comments on commit 91b11da

Please sign in to comment.