Skip to content

Commit 73ce4d0

Browse files
authored
Merge pull request #686 from moredure/master
Utilise byteSlice as a part of struct to avoid allocations
2 parents e7478b1 + e849204 commit 73ce4d0

File tree

6 files changed

+73
-50
lines changed

6 files changed

+73
-50
lines changed

client/conn.go

+9-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package client
22

33
import (
4+
"bytes"
45
"context"
56
"crypto/tls"
67
"fmt"
@@ -223,30 +224,30 @@ func (c *Conn) ExecuteMultiple(query string, perResultCallback ExecPerResultCall
223224
return nil, errors.Trace(err)
224225
}
225226

226-
var buf []byte
227227
var err error
228228
var result *Result
229-
defer utils.ByteSlicePut(buf)
229+
230+
bs := utils.ByteSliceGet(16)
231+
defer utils.ByteSlicePut(bs)
230232

231233
for {
232-
buf, err = c.ReadPacketReuseMem(utils.ByteSliceGet(16)[:0])
234+
bs.B, err = c.ReadPacketReuseMem(bs.B[:0])
233235
if err != nil {
234236
return nil, errors.Trace(err)
235237
}
236238

237-
switch buf[0] {
239+
switch bs.B[0] {
238240
case OK_HEADER:
239-
result, err = c.handleOKPacket(buf)
241+
result, err = c.handleOKPacket(bs.B)
240242
case ERR_HEADER:
241-
err = c.handleErrorPacket(append([]byte{}, buf...))
243+
err = c.handleErrorPacket(bytes.Repeat(bs.B, 1))
242244
result = nil
243245
case LocalInFile_HEADER:
244246
err = ErrMalformPacket
245247
result = nil
246248
default:
247-
result, err = c.readResultset(buf, false)
249+
result, err = c.readResultset(bs.B, false)
248250
}
249-
250251
// call user-defined callback
251252
perResultCallback(result, err)
252253

client/req.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ func (c *Conn) writeCommandBuf(command byte, arg []byte) error {
2121

2222
length := len(arg) + 1
2323
data := utils.ByteSliceGet(length + 4)
24-
data[4] = command
24+
data.B[4] = command
2525

26-
copy(data[5:], arg)
26+
copy(data.B[5:], arg)
2727

28-
err := c.WritePacket(data)
28+
err := c.WritePacket(data.B)
2929

3030
utils.ByteSlicePut(data)
3131

client/resp.go

+24-20
Original file line numberDiff line numberDiff line change
@@ -216,38 +216,42 @@ func (c *Conn) readOK() (*Result, error) {
216216
}
217217

218218
func (c *Conn) readResult(binary bool) (*Result, error) {
219-
firstPkgBuf, err := c.ReadPacketReuseMem(utils.ByteSliceGet(16)[:0])
220-
defer utils.ByteSlicePut(firstPkgBuf)
221-
219+
bs := utils.ByteSliceGet(16)
220+
defer utils.ByteSlicePut(bs)
221+
var err error
222+
bs.B, err = c.ReadPacketReuseMem(bs.B[:0])
222223
if err != nil {
223224
return nil, errors.Trace(err)
224225
}
225226

226-
if firstPkgBuf[0] == OK_HEADER {
227-
return c.handleOKPacket(firstPkgBuf)
228-
} else if firstPkgBuf[0] == ERR_HEADER {
229-
return nil, c.handleErrorPacket(append([]byte{}, firstPkgBuf...))
230-
} else if firstPkgBuf[0] == LocalInFile_HEADER {
227+
switch bs.B[0] {
228+
case OK_HEADER:
229+
return c.handleOKPacket(bs.B)
230+
case ERR_HEADER:
231+
return nil, c.handleErrorPacket(bytes.Repeat(bs.B, 1))
232+
case LocalInFile_HEADER:
231233
return nil, ErrMalformPacket
234+
default:
235+
return c.readResultset(bs.B, binary)
232236
}
233-
234-
return c.readResultset(firstPkgBuf, binary)
235237
}
236238

237239
func (c *Conn) readResultStreaming(binary bool, result *Result, perRowCb SelectPerRowCallback, perResCb SelectPerResultCallback) error {
238-
firstPkgBuf, err := c.ReadPacketReuseMem(utils.ByteSliceGet(16)[:0])
239-
defer utils.ByteSlicePut(firstPkgBuf)
240-
240+
bs := utils.ByteSliceGet(16)
241+
defer utils.ByteSlicePut(bs)
242+
var err error
243+
bs.B, err = c.ReadPacketReuseMem(bs.B[:0])
241244
if err != nil {
242245
return errors.Trace(err)
243246
}
244247

245-
if firstPkgBuf[0] == OK_HEADER {
248+
switch bs.B[0] {
249+
case OK_HEADER:
246250
// https://dev.mysql.com/doc/internals/en/com-query-response.html
247251
// 14.6.4.1 COM_QUERY Response
248252
// If the number of columns in the resultset is 0, this is a OK_Packet.
249253

250-
okResult, err := c.handleOKPacket(firstPkgBuf)
254+
okResult, err := c.handleOKPacket(bs.B)
251255
if err != nil {
252256
return errors.Trace(err)
253257
}
@@ -262,13 +266,13 @@ func (c *Conn) readResultStreaming(binary bool, result *Result, perRowCb SelectP
262266
result.Reset(0)
263267
}
264268
return nil
265-
} else if firstPkgBuf[0] == ERR_HEADER {
266-
return c.handleErrorPacket(append([]byte{}, firstPkgBuf...))
267-
} else if firstPkgBuf[0] == LocalInFile_HEADER {
269+
case ERR_HEADER:
270+
return c.handleErrorPacket(bytes.Repeat(bs.B, 1))
271+
case LocalInFile_HEADER:
268272
return ErrMalformPacket
273+
default:
274+
return c.readResultsetStreaming(bs.B, binary, result, perRowCb, perResCb)
269275
}
270-
271-
return c.readResultsetStreaming(firstPkgBuf, binary, result, perRowCb, perResCb)
272276
}
273277

274278
func (c *Conn) readResultset(data []byte, binary bool) (*Result, error) {

utils/byte_slice_pool.go

+13-19
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,29 @@ package utils
22

33
import "sync"
44

5+
type ByteSlice struct {
6+
B []byte
7+
}
8+
59
var (
610
byteSlicePool = sync.Pool{
711
New: func() interface{} {
8-
return []byte{}
12+
return new(ByteSlice)
913
},
1014
}
11-
byteSliceChan = make(chan []byte, 10)
1215
)
1316

14-
func ByteSliceGet(length int) (data []byte) {
15-
select {
16-
case data = <-byteSliceChan:
17-
default:
18-
data = byteSlicePool.Get().([]byte)[:0]
19-
}
20-
21-
if cap(data) < length {
22-
data = make([]byte, length)
17+
func ByteSliceGet(length int) *ByteSlice {
18+
data := byteSlicePool.Get().(*ByteSlice)
19+
if cap(data.B) < length {
20+
data.B = make([]byte, length)
2321
} else {
24-
data = data[:length]
22+
data.B = data.B[:length]
2523
}
26-
2724
return data
2825
}
2926

30-
func ByteSlicePut(data []byte) {
31-
select {
32-
case byteSliceChan <- data:
33-
default:
34-
byteSlicePool.Put(data) //nolint:staticcheck
35-
}
27+
func ByteSlicePut(data *ByteSlice) {
28+
data.B = data.B[:0]
29+
byteSlicePool.Put(data)
3630
}

utils/byte_slice_pool_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package utils
2+
3+
import "testing"
4+
5+
func BenchmarkByteSlicePool(b *testing.B) {
6+
b.ReportAllocs()
7+
for i := 0; i < b.N; i++ {
8+
b := ByteSliceGet(16)
9+
b.B = append(b.B[:0], 0, 1)
10+
ByteSlicePut(b)
11+
}
12+
}

utils/bytes_buffer_pool_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package utils
2+
3+
import "testing"
4+
5+
func BenchmarkBytesBufferPool(b *testing.B) {
6+
b.ReportAllocs()
7+
for i := 0; i < b.N; i++ {
8+
b := BytesBufferGet()
9+
b.WriteString("01")
10+
BytesBufferPut(b)
11+
}
12+
}

0 commit comments

Comments
 (0)