From 939c50e1128e5c692a1a1a274b20d90a1cb064a9 Mon Sep 17 00:00:00 2001 From: azihsoyn Date: Wed, 30 Nov 2016 18:41:56 +0900 Subject: [PATCH] retry performance tuning --- benchmark/benchmark_test.go | 11 ++++-- distinct.go | 19 +++++----- distinct_test.go | 24 ++++++------- example_test.go | 2 +- examples/distinct/main.go | 2 +- gollection.go | 69 ++++++++++++++++++++++++++++++++++--- 6 files changed, 98 insertions(+), 29 deletions(-) diff --git a/benchmark/benchmark_test.go b/benchmark/benchmark_test.go index 1153d3f..50a761e 100644 --- a/benchmark/benchmark_test.go +++ b/benchmark/benchmark_test.go @@ -14,6 +14,13 @@ func BenchmarkNew(b *testing.B) { } } +func BenchmarkNew2(b *testing.B) { + b.ResetTimer() + for i := 0; i < b.N; i++ { + gollection.New2([]int{}) + } +} + func BenchmarkNewAndResult(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { @@ -32,7 +39,7 @@ func BenchmarkNewAndResultAs(b *testing.B) { func BenchmarkNewStream(b *testing.B) { b.ResetTimer() for i := 0; i < b.N; i++ { - gollection.NewStream([]int{}).Result() + gollection.NewStream([]int{}) } } @@ -40,7 +47,7 @@ func BenchmarkDistinct(b *testing.B) { arr := []int{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9} b.ResetTimer() for i := 0; i < b.N; i++ { - gollection.New(arr).Distinct().Result() + gollection.New2(arr).Distinct() } } diff --git a/distinct.go b/distinct.go index a514f95..47a8602 100644 --- a/distinct.go +++ b/distinct.go @@ -28,23 +28,24 @@ func (g *gollection) DistinctBy(f /*func(v ) */ interface{}) *gollection } func (g *gollection) distinct() *gollection { - sv, err := g.validateSlice("Distinct") - if err != nil { - return &gollection{err: err} + defer g.slice2.free() + if g.err != nil { + return &gollection{err: g.err} } - ret := reflect.MakeSlice(sv.Type(), 0, sv.Len()) m := make(map[interface{}]bool) + ret := newInterfaceSlice() - for i := 0; i < sv.Len(); i++ { - v := sv.Index(i) - if processDistinct(v.Interface(), m) { - ret = reflect.Append(ret, v) + for i := 0; i < len(g.slice2.slice); i++ { + v := g.slice2.slice[i] + if processDistinct(v, m) { + ret.slice = append(ret.slice, v) } } return &gollection{ - slice: ret.Interface(), + slice2: ret, + meta: g.meta, } } diff --git a/distinct_test.go b/distinct_test.go index 9a34dd8..2305dff 100644 --- a/distinct_test.go +++ b/distinct_test.go @@ -12,24 +12,24 @@ func TestDistinct(t *testing.T) { arr := []int{1, 2, 3, 1, 2, 3} expect := []int{1, 2, 3} - res, err := gollection.New(arr).Distinct().Result() + res, err := gollection.New2(arr).Distinct().Result() assert.NoError(err) assert.Equal(expect, res) } func TestDistinct_NotSlice(t *testing.T) { assert := assert.New(t) - _, err := gollection.New("not slice value").Distinct().Result() + _, err := gollection.New2("not slice value").Distinct().Result() assert.Error(err) } func TestDistinct_HavingError(t *testing.T) { assert := assert.New(t) - _, err := gollection.New("not slice value").Distinct().Distinct().Result() + _, err := gollection.New2("not slice value").Distinct().Distinct().Result() assert.Error(err) res := []int{} - err = gollection.New("not slice value").Distinct().Distinct().ResultAs(&res) + err = gollection.New2("not slice value").Distinct().Distinct().ResultAs(&res) assert.Error(err) } @@ -61,11 +61,11 @@ func TestDistinctBy_HavingError(t *testing.T) { assert := assert.New(t) _, err := gollection.New("not slice value"). DistinctBy(func(v interface{}) interface{} { - return v - }). + return v + }). DistinctBy(func(v interface{}) interface{} { - return v - }). + return v + }). Result() assert.Error(err) } @@ -120,11 +120,11 @@ func TestDistinctBy_Stream_HavingError(t *testing.T) { assert := assert.New(t) _, err := gollection.NewStream("not slice value"). DistinctBy(func(v interface{}) interface{} { - return v - }). + return v + }). DistinctBy(func(v interface{}) interface{} { - return v - }). + return v + }). Result() assert.Error(err) } diff --git a/example_test.go b/example_test.go index be6aaaa..1311b92 100644 --- a/example_test.go +++ b/example_test.go @@ -9,7 +9,7 @@ import ( func Example_distinct() { arr := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10} - res, err := gollection.New(arr).Distinct().Result() + res, err := gollection.New2(arr).Distinct().Result() fmt.Println(res, err) // Output: [1 2 3 4 5 6 7 8 9 10] } diff --git a/examples/distinct/main.go b/examples/distinct/main.go index 5877c6b..0dd5c6b 100644 --- a/examples/distinct/main.go +++ b/examples/distinct/main.go @@ -9,7 +9,7 @@ import ( func main() { arr := []int{1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10} - res, _ := gollection.New(arr).Distinct().Result() + res, _ := gollection.New2(arr).Distinct().Result() fmt.Println("origin : ", arr) fmt.Println("ret : ", res) // {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} diff --git a/gollection.go b/gollection.go index 7df89b9..a19627d 100644 --- a/gollection.go +++ b/gollection.go @@ -9,11 +9,44 @@ import ( "sync" ) +type interfaceSlice struct { + slice []interface{} +} + +func (s *interfaceSlice) truncate() { + s.slice = s.slice[:0] +} + +func (s *interfaceSlice) free() { + slicePool.Put(s) +} + +func newInterfaceSlice() *interfaceSlice { + s := slicePool.Get().(*interfaceSlice) + s.truncate() + return s +} + +var slicePool = sync.Pool{ + New: func() interface{} { + return &interfaceSlice{ + slice: make([]interface{}, 0, 10), + } + }, +} + type gollection struct { - slice interface{} - val interface{} - ch chan interface{} - err error + slice interface{} + val interface{} + ch chan interface{} + err error + slice2 *interfaceSlice + meta meta +} + +type meta struct { + Len int + Type reflect.Type } // New returns a gollection instance which can method chain *sequentially* specified by some type of slice. @@ -23,11 +56,39 @@ func New(slice interface{}) *gollection { } } +func New2(slice interface{}) *gollection { + sv := reflect.ValueOf(slice) + if sv.Kind() != reflect.Slice { + return &gollection{err: fmt.Errorf("gollection.%s called with non-slice value of type %T", slice)} + } + s := newInterfaceSlice() + + for i := 0; i < sv.Len(); i++ { + v := sv.Index(i).Interface() + s.slice = append(s.slice, v) + } + return &gollection{ + slice: slice, + slice2: s, + meta: meta{ + Len: sv.Len(), + Type: sv.Type(), + }, + } +} + // Result return a collection processed value and error. func (g *gollection) Result() (interface{}, error) { if g.ch != nil { return g.resultStream() } + if g.slice2 != nil { + ret := reflect.MakeSlice(g.meta.Type, 0, g.meta.Len) + for _, v := range g.slice2.slice { + ret = reflect.Append(ret, reflect.ValueOf(v)) + } + return ret.Interface(), g.err + } return g.result() }