gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit d096375da20dd060cb672ea511e0c993d6e115a2
parent 6614f5b29388bba644bfdbc36cc3d62b42459d09
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Wed,  9 May 2018 23:10:23 +0800

Merge pull request #24 from francoispqt/update/switch-to-sync-pool

Update/switch to sync pool
Diffstat:
Mdecode_pool.go | 70+++++++++++++++++++++++-----------------------------------------------
Mdecode_pool_test.go | 14--------------
Mdecode_stream_pool.go | 53++++++++++++++++++++++-------------------------------
Mdecode_stream_pool_test.go | 47-----------------------------------------------
Mdecode_string_test.go | 7++++++-
Mencode_pool.go | 57++++++++++++++++++++++++++-------------------------------
Mencode_pool_test.go | 19-------------------
Mencode_stream.go | 7++-----
Mencode_stream_pool.go | 34+++++++++++++---------------------
Dencode_stream_pool_test.go | 21---------------------
10 files changed, 92 insertions(+), 237 deletions(-)

diff --git a/decode_pool.go b/decode_pool.go @@ -1,25 +1,19 @@ package gojay -import "io" +import ( + "io" + "sync" +) -var decPool = make(chan *Decoder, 32) +var decPool = sync.Pool{ + New: func() interface{} { + return NewDecoder(nil) + }, +} func init() { -initStreamDecPool: - for { - select { - case streamDecPool <- Stream.NewDecoder(nil): - default: - break initStreamDecPool - } - } -initDecPool: - for { - select { - case decPool <- NewDecoder(nil): - default: - break initDecPool - } + for i := 0; i < 32; i++ { + decPool.Put(NewDecoder(nil)) } } @@ -45,33 +39,18 @@ func BorrowDecoder(r io.Reader) *Decoder { return borrowDecoder(r, 512) } func borrowDecoder(r io.Reader, bufSize int) *Decoder { - select { - case dec := <-decPool: - dec.called = 0 - dec.keysDone = 0 - dec.cursor = 0 - dec.err = nil - dec.r = r - dec.length = 0 - dec.isPooled = 0 - if bufSize > 0 { - dec.data = make([]byte, bufSize) - } - return dec - default: - dec := &Decoder{ - called: 0, - cursor: 0, - keysDone: 0, - err: nil, - r: r, - isPooled: 0, - } - if bufSize > 0 { - dec.data = make([]byte, bufSize) - } - return dec + dec := decPool.Get().(*Decoder) + dec.called = 0 + dec.keysDone = 0 + dec.cursor = 0 + dec.err = nil + dec.r = r + dec.length = 0 + dec.isPooled = 0 + if bufSize > 0 { + dec.data = make([]byte, bufSize) } + return dec } // Release sends back a Decoder to the pool. @@ -79,8 +58,5 @@ func borrowDecoder(r io.Reader, bufSize int) *Decoder { // a panic will be raised with an InvalidUsagePooledDecoderError error. func (dec *Decoder) Release() { dec.isPooled = 1 - select { - case decPool <- dec: - default: - } + decPool.Put(dec) } diff --git a/decode_pool_test.go b/decode_pool_test.go @@ -1,25 +1,11 @@ package gojay import ( - "strings" "testing" "github.com/stretchr/testify/assert" ) -func TestDecoderBorrowFromPool(t *testing.T) { - // reset pool - decPool = make(chan *Decoder, 16) - // borrow decoder - dec := BorrowDecoder(strings.NewReader("")) - // release - dec.Release() - // get from pool - nDec := BorrowDecoder(strings.NewReader("")) - // assert same - assert.Equal(t, dec, nDec, "both decoders should be the same") -} - func TestDecoderBorrowFromPoolSetBuffSize(t *testing.T) { dec := borrowDecoder(nil, 512) assert.Len(t, dec.data, 512, "data buffer should be of len 512") diff --git a/decode_stream_pool.go b/decode_stream_pool.go @@ -1,8 +1,15 @@ package gojay -import "io" +import ( + "io" + "sync" +) -var streamDecPool = make(chan *StreamDecoder, 32) +var streamDecPool = sync.Pool{ + New: func() interface{} { + return Stream.NewDecoder(nil) + }, +} // NewDecoder returns a new StreamDecoder. // It takes an io.Reader implementation as data input. @@ -26,32 +33,19 @@ func (s stream) BorrowDecoder(r io.Reader) *StreamDecoder { } func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder { - select { - case streamDec := <-streamDecPool: - streamDec.called = 0 - streamDec.keysDone = 0 - streamDec.cursor = 0 - streamDec.err = nil - streamDec.r = r - streamDec.length = 0 - streamDec.isPooled = 0 - streamDec.done = make(chan struct{}, 1) - if bufSize > 0 { - streamDec.data = make([]byte, bufSize) - } - return streamDec - default: - dec := NewDecoder(r) - if bufSize > 0 { - dec.data = make([]byte, bufSize) - dec.length = 0 - } - streamDec := &StreamDecoder{ - Decoder: dec, - done: make(chan struct{}, 1), - } - return streamDec + streamDec := streamDecPool.Get().(*StreamDecoder) + streamDec.called = 0 + streamDec.keysDone = 0 + streamDec.cursor = 0 + streamDec.err = nil + streamDec.r = r + streamDec.length = 0 + streamDec.isPooled = 0 + streamDec.done = make(chan struct{}, 1) + if bufSize > 0 { + streamDec.data = make([]byte, bufSize) } + return streamDec } // Release sends back a Decoder to the pool. @@ -59,8 +53,5 @@ func (s stream) borrowDecoder(r io.Reader, bufSize int) *StreamDecoder { // a panic will be raised with an InvalidUsagePooledDecoderError error. func (dec *StreamDecoder) Release() { dec.isPooled = 1 - select { - case streamDecPool <- dec: - default: - } + streamDecPool.Put(dec) } diff --git a/decode_stream_pool_test.go b/decode_stream_pool_test.go @@ -6,53 +6,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDecodeStreamBorrow(t *testing.T) { - // we override the pool chan - streamDecPool = make(chan *StreamDecoder, 1) - // add one decoder to the channel - dec := Stream.NewDecoder(nil) - streamDecPool <- dec - // borrow one decoder to the channel - nDec := Stream.BorrowDecoder(nil) - // make sure they are the same - assert.Equal(t, dec, nDec, "decoder added to the pool and new decoder should be the same") -} - -func TestDecodeStreamBorrow1(t *testing.T) { - // we override the pool chan - streamDecPool = make(chan *StreamDecoder, 1) - // add one decoder to the channel - dec := Stream.NewDecoder(nil) - streamDecPool <- dec - // reset streamDecPool - streamDecPool = make(chan *StreamDecoder, 1) - // borrow one decoder to the channel - nDec := Stream.BorrowDecoder(nil) - // make sure they are the same - assert.NotEqual(t, dec, nDec, "decoder added to the pool and new decoder should be the same") -} -func TestDecodeStreamBorrow2(t *testing.T) { - // we override the pool chan - streamDecPool = make(chan *StreamDecoder, 1) - // add one decoder to the channel - dec := Stream.NewDecoder(nil) - dec.data = make([]byte, 128) - streamDecPool <- dec - // borrow one decoder to the channel - nDec := Stream.BorrowDecoder(nil) - // make sure they are the same - assert.Equal(t, dec, nDec, "decoder added to the pool and new decoder should be the same") - assert.Equal(t, 512, len(nDec.data), "len of dec.data should be 512") -} -func TestDecodeStreamBorrow3(t *testing.T) { - // we override the pool chan - streamDecPool = make(chan *StreamDecoder, 16) - // borrow one decoder to the channel - nDec := Stream.BorrowDecoder(nil) - // make sure they are the same - assert.Equal(t, 512, len(nDec.data), "len of dec.data should be 512") -} - func TestDecodeStreamDecodePooledDecoderError(t *testing.T) { // we override the pool chan dec := Stream.NewDecoder(nil) diff --git a/decode_string_test.go b/decode_string_test.go @@ -2,6 +2,7 @@ package gojay import ( "strings" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -58,7 +59,11 @@ func TestDecoderStringDecoderAPI(t *testing.T) { func TestDecoderStringPoolError(t *testing.T) { // reset the pool to make sure it's not full - decPool = make(chan *Decoder, 16) + decPool = sync.Pool{ + New: func() interface{} { + return NewDecoder(nil) + }, + } result := "" dec := NewDecoder(nil) dec.Release() diff --git a/encode_pool.go b/encode_pool.go @@ -1,26 +1,28 @@ package gojay -import "io" +import ( + "io" + "sync" +) -var encPool = make(chan *Encoder, 32) -var streamEncPool = make(chan *StreamEncoder, 32) +var encPool = sync.Pool{ + New: func() interface{} { + return NewEncoder(nil) + }, +} + +var streamEncPool = sync.Pool{ + New: func() interface{} { + return Stream.NewEncoder(nil) + }, +} func init() { -initStreamEncPool: - for { - select { - case streamEncPool <- Stream.NewEncoder(nil): - default: - break initStreamEncPool - } + for i := 0; i < 32; i++ { + encPool.Put(NewEncoder(nil)) } -initEncPool: - for { - select { - case encPool <- NewEncoder(nil): - default: - break initEncPool - } + for i := 0; i < 32; i++ { + streamEncPool.Put(Stream.NewEncoder(nil)) } } @@ -34,23 +36,16 @@ func newEncoder() *Encoder { // BorrowEncoder borrows an Encoder from the pool. func BorrowEncoder(w io.Writer) *Encoder { - select { - case enc := <-encPool: - enc.isPooled = 0 - enc.w = w - enc.err = nil - return enc - default: - return &Encoder{w: w, buf: make([]byte, 0, 512)} - } + enc := encPool.Get().(*Encoder) + enc.w = w + enc.buf = enc.buf[:0] + enc.isPooled = 0 + enc.err = nil + return enc } // Release sends back a Encoder to the pool. func (enc *Encoder) Release() { - enc.buf = enc.buf[:0] enc.isPooled = 1 - select { - case encPool <- enc: - default: - } + encPool.Put(enc) } diff --git a/encode_pool_test.go b/encode_pool_test.go @@ -1,20 +1 @@ package gojay - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEncoderNewFromPool(t *testing.T) { - // reset pool - encPool = make(chan *Encoder, 16) - // get new Encoder - enc := NewEncoder(nil) - // add to pool - enc.Release() - // borrow encoder - nEnc := BorrowEncoder(nil) - // make sure it's the same - assert.Equal(t, enc, nEnc, "enc and nEnc from pool should be the same") -} diff --git a/encode_stream.go b/encode_stream.go @@ -74,11 +74,8 @@ func (s *StreamEncoder) NConsumer(n int) *StreamEncoder { // If a decoder is used after calling Release // a panic will be raised with an InvalidUsagePooledDecoderError error. func (s *StreamEncoder) Release() { - s.Encoder.isPooled = 1 - select { - case streamEncPool <- s: - default: - } + s.isPooled = 1 + streamEncPool.Put(s) } // Done returns a channel that's closed when work is done. diff --git a/encode_stream_pool.go b/encode_stream_pool.go @@ -16,28 +16,20 @@ func (s stream) NewEncoder(w io.Writer) *StreamEncoder { // // If no StreamEncoder is available in the pool, it returns a fresh one func (s stream) BorrowEncoder(w io.Writer) *StreamEncoder { - select { - case streamEnc := <-streamEncPool: - streamEnc.isPooled = 0 - streamEnc.w = w - streamEnc.Encoder.err = nil - streamEnc.done = make(chan struct{}, 1) - streamEnc.Encoder.buf = make([]byte, 0, 512) - streamEnc.nConsumer = 1 - return streamEnc - default: - return s.NewEncoder(w) - } + streamEnc := streamEncPool.Get().(*StreamEncoder) + streamEnc.w = w + streamEnc.Encoder.err = nil + streamEnc.done = make(chan struct{}, 1) + streamEnc.Encoder.buf = streamEnc.buf[:0] + streamEnc.nConsumer = 1 + streamEnc.isPooled = 0 + return streamEnc } func (s stream) borrowEncoder(w io.Writer) *StreamEncoder { - select { - case streamEnc := <-streamEncPool: - streamEnc.isPooled = 0 - streamEnc.w = w - streamEnc.Encoder.err = nil - return streamEnc - default: - return s.NewEncoder(w) - } + streamEnc := streamEncPool.Get().(*StreamEncoder) + streamEnc.isPooled = 0 + streamEnc.w = w + streamEnc.Encoder.err = nil + return streamEnc } diff --git a/encode_stream_pool_test.go b/encode_stream_pool_test.go @@ -1,21 +0,0 @@ -package gojay - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestEncodeStreamBorrow1(t *testing.T) { - // we override the pool chan - streamEncPool = make(chan *StreamEncoder, 1) - // add one decoder to the channel - enc := Stream.NewEncoder(nil) - streamEncPool <- enc - // reset streamEncPool - streamEncPool = make(chan *StreamEncoder, 1) - // borrow one decoder to the channel - nEnc := Stream.BorrowEncoder(nil) - // make sure they are the same - assert.NotEqual(t, enc, nEnc, "encoder added to the pool and new decoder should be the same") -}