From 75300c50b56adc38eebc50f3286fbc5e715948c9 Mon Sep 17 00:00:00 2001 From: Christian Date: Sun, 6 Jan 2019 12:32:01 -0800 Subject: [PATCH] Simplified push method; updated first and max slice sizes --- README.md | 10 +-- stack.go | 58 ++++----------- testdata/stack.jpg | Bin 10045 -> 9215 bytes unit_test.go | 172 +++------------------------------------------ 4 files changed, 26 insertions(+), 214 deletions(-) diff --git a/README.md b/README.md index 635bdcf..dddfce1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # stack [![Build Status](https://travis-ci.com/ef-ds/stack.svg?branch=master)](https://travis-ci.com/ef-ds/stack) [![codecov](https://codecov.io/gh/ef-ds/stack/branch/master/graph/badge.svg)](https://codecov.io/gh/ef-ds/stack) [![Go Report Card](https://goreportcard.com/badge/github.com/ef-ds/stack)](https://goreportcard.com/report/github.com/ef-ds/stack) [![GoDoc](https://godoc.org/github.com/ef-ds/stack?status.svg)](https://godoc.org/github.com/ef-ds/stack) -Package stack implements a very fast and efficient general purpose Last-In-First-Out (LIFO) stack data structure that is specifically optimized to perform when used by Microservices and serverless services running in production environments. Internally, stack stores the elements in a dynamic growing semi-circular doubly linked list of arrays. +Package stack implements a very fast and efficient general purpose Last-In-First-Out (LIFO) stack data structure that is specifically optimized to perform when used by Microservices and serverless services running in production environments. Internally, stack stores the elements in a dynamic growing semi-circular inverted singly linked list of arrays. ## Install @@ -64,7 +64,7 @@ See the [benchmark tests](https://github.com/ef-ds/stack-bench-tests/blob/master ## Performance -Stack has constant time (O(1)) on all its operations (Push/Pop/Back/Len). It's not amortized constant because stack never copies more than 256 (maxInternalSliceSize/sliceGrowthFactor) items and when it expands or grow, it never does so by more than 1024 (maxInternalSliceSize) items in a single operation. +Stack has constant time (O(1)) on all its operations (Push/Pop/Back/Len). It's not amortized constant because stack never copies more than 256 (maxInternalSliceSize/2) items and when it expands or grow, it never does so by more than 512 (maxInternalSliceSize) items in a single operation. Stack offers either the best or very competitive performance across all test sets, suites and ranges. @@ -74,9 +74,9 @@ See [performance](https://github.com/ef-ds/stack-bench-tests/blob/master/PERFORM ## Design -The Efficient Data Structures (ef-ds) stack employs a new, modern stack design: a semi-circular shaped, linked slices design. +The Efficient Data Structures (ef-ds) stack employs a new, modern stack design: a dynamic growing semi-circular inverted singly linked list of slices. -That means the [LIFO stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) is a [doubly-linked list](https://en.wikipedia.org/wiki/Doubly_linked_list) where each node value is a fixed size [slice](https://tour.golang.org/moretypes/7). It is semi-circular in shape because the first node in the linked list points to itself, but the last one points to nil. +That means the [LIFO stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)) is a [singly-linked list](https://en.wikipedia.org/wiki/Singly_linked_list) where each node value is a fixed size [slice](https://tour.golang.org/moretypes/7). It is inverted singly linked list because each node points only to the previous one (instead of next) and it is semi-circular in shape because the first node in the linked list points to itself, but the last one points to nil. ![ns/op](testdata/stack.jpg?raw=true "stack Design") @@ -151,7 +151,7 @@ One sofware engineer can't change the world him/herself, but a whole bunch of us ## Competition -We're extremely interested in improving stack. Please let us know your suggestions for possible improvements and if you know of other high performance stacks not tested here, let us know and we're very glad to benchmark them. +We're extremely interested in improving stack and we're on an endless quest for better efficiency and more performance. Please let us know your suggestions for possible improvements and if you know of other high performance stacks not tested here, let us know and we're very glad to benchmark them. ## Releases diff --git a/stack.go b/stack.go index 041faad..fbbeb0d 100644 --- a/stack.go +++ b/stack.go @@ -26,29 +26,16 @@ package stack const ( // firstSliceSize holds the size of the first slice. - firstSliceSize = 4 - - // sliceGrowthFactor determines by how much and how fast the first internal - // slice should grow. A growth factor of 4, firstSliceSize = 4 and - // maxInternalSliceSize = 256, the first slice will start with size 4, - // then 16 (4*4), then 64 (16*4), then 256 (64*4), then 1024 (256*4). - // The growth factor should be tweaked together with firstSliceSize and - // maxInternalSliceSize and for maximum efficiency. - // sliceGrowthFactor only applies to the very first slice creates. All other - // subsequent slices are created with fixed size of maxInternalSliceSize. - sliceGrowthFactor = 4 + firstSliceSize = 8 // maxInternalSliceSize holds the maximum size of each internal slice. - maxInternalSliceSize = 1024 + maxInternalSliceSize = 512 ) // Stack implements an unbounded, dynamically growing Last-In-First-Out (LIFO) // stack data structure. // The zero value for stack is an empty stack ready to use. type Stack struct { - // Head points to the first node of the linked list. - head *node - // Tail points to the last node of the linked list. // In an empty stack, head and tail points to the same node. tail *node @@ -63,9 +50,6 @@ type node struct { // v holds the list of user added values in this node. v []interface{} - // n points to the next node in the linked list. - n *node - // p points to the previous node in the linked list. p *node } @@ -99,31 +83,14 @@ func (s *Stack) Back() (interface{}, bool) { // Push adds value v to the the back of the stack. // The complexity is O(1). func (s *Stack) Push(v interface{}) { - switch { - case s.head == nil: - // No nodes present yet. - h := &node{v: make([]interface{}, 0, firstSliceSize)} - h.p = h - s.head = h - s.tail = h - case len(s.tail.v) < cap(s.tail.v): - // There's room in the tail slice. - case cap(s.tail.v) < maxInternalSliceSize: - // We're on the first slice and it hasn't grown large enough yet. - l := len(s.tail.v) - nv := make([]interface{}, l, l*sliceGrowthFactor) - copy(nv, s.tail.v) - s.tail.v = nv - case s.tail.n != nil: - // There's at least one unused slice between head and tail nodes. - n := s.tail.n - s.tail = n - default: - // No available nodes, so make one. - n := &node{v: make([]interface{}, 0, maxInternalSliceSize)} - n.p = s.tail - s.tail.n = n - s.tail = n + if s.tail == nil { + s.tail = &node{v: make([]interface{}, 0, firstSliceSize)} + s.tail.p = s.tail + } else if len(s.tail.v) >= maxInternalSliceSize { + s.tail = &node{ + v: make([]interface{}, 0, maxInternalSliceSize), + p: s.tail, + } } s.len++ s.tail.v = append(s.tail.v, v) @@ -137,6 +104,7 @@ func (s *Stack) Pop() (interface{}, bool) { if s.len == 0 { return nil, false } + s.len-- tp := len(s.tail.v) - 1 vp := &s.tail.v[tp] @@ -144,9 +112,7 @@ func (s *Stack) Pop() (interface{}, bool) { *vp = nil // Avoid memory leaks s.tail.v = s.tail.v[:tp] if tp <= 0 { - // Move to the previous slice as all elements - // in the current one were removed. - s.tail = s.tail.p + s.tail = s.tail.p // Move to the previous slice. } return v, true } diff --git a/testdata/stack.jpg b/testdata/stack.jpg index 4aa1d6cdad8cafbca995007a5ebb6b1180451cc7..97bc3f82472e71fd4fd4c8e8366beb4a8e8a0d9e 100644 GIT binary patch literal 9215 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 literal 10045 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 expectedHeadSliceSize { - expectedHeadSliceSize *= sliceGrowthFactor - } } // Push 1 extra item to force the creation of a new array pushValue++ s.Push(pushValue) extraAddedItems++ - checkLinks(t, s, pushValue, maxInternalSliceSize, maxInternalSliceSize, s.tail, s.head, nil, s.head) + checkLinks(t, s, pushValue, maxInternalSliceSize) // Push another maxInternalSliceSize-1 to fill the second array for i := 1; i <= maxInternalSliceSize-1; i++ { pushValue++ s.Push(pushValue) - checkLinks(t, s, pushValue, maxInternalSliceSize, maxInternalSliceSize, s.tail, s.head, nil, s.head) + checkLinks(t, s, pushValue, maxInternalSliceSize) } // Push 1 extra item to force the creation of a new array (3 total) pushValue++ s.Push(pushValue) - checkLinks(t, s, pushValue, maxInternalSliceSize, maxInternalSliceSize, s.tail.p, s.head, nil, s.head.n) - /// Check middle links - if s.head.n.n != s.tail { - t.Error("Expected: s.head.n.n == s.tail; Got: s.head.n.n != s.tail") - } - if s.head.n.p != s.head { - t.Error("Expected: s.head.n.p == s.head; Got: s.head.n.p != s.head") - } + checkLinks(t, s, pushValue, maxInternalSliceSize) // Check final len after all pushes if s.Len() != maxInternalSliceSize+maxInternalSliceSize+extraAddedItems { @@ -101,14 +88,7 @@ func TestPushPopShouldHaveAllInternalLinksInARing(t *testing.T) { t.Errorf("Expected: %d; Got: %d", popValue, v) } popValue-- - checkLinks(t, s, popValue, maxInternalSliceSize, maxInternalSliceSize, s.tail, s.head, s.tail.n, s.head) - //Check last slice links (not tail anymore; tail is the middle one) - if s.tail.n.n != nil { - t.Error("Expected: s.tail.n.n == nil; Got: s.tail.n.n != nil") - } - if s.tail.n.p != s.tail { - t.Error("Expected: s.tail.n.p == s.tail; Got: s.tail.n.p != s.tail") - } + checkLinks(t, s, popValue, maxInternalSliceSize) // Pop maxInternalSliceSize-1 items to empty the tail (middle) slice for i := 1; i <= maxInternalSliceSize-1; i++ { @@ -116,14 +96,7 @@ func TestPushPopShouldHaveAllInternalLinksInARing(t *testing.T) { t.Errorf("Expected: %d; Got: %d", popValue, v) } popValue-- - checkLinks(t, s, popValue, maxInternalSliceSize, maxInternalSliceSize, s.tail, s.head, s.tail.n, s.head) - /// Check last slice links - if s.tail.n.n != nil { - t.Error("Expected: s.tail.n.n == nil; Got: s.tail.n.n != nil") - } - if s.tail.n.p != s.tail { - t.Error("Expected: s.tail.n.p == s.tail; Got: s.tail.n.p != s.tail") - } + checkLinks(t, s, popValue, maxInternalSliceSize) } // Pop one extra item to force moving the tail to the head (first) slice. This also means the old tail @@ -132,21 +105,7 @@ func TestPushPopShouldHaveAllInternalLinksInARing(t *testing.T) { t.Errorf("Expected: %d; Got: %d", popValue, v) } popValue-- - checkLinks(t, s, popValue, maxInternalSliceSize, maxInternalSliceSize, s.tail.n, s.head, s.tail.n, s.head) - /// Check middle links - if s.head.n.n != s.tail.n.n { - t.Error("Expected: s.head.n.n == s.tail.n.n; Got: s.head.n.n != s.tail.n.n") - } - if s.head.n.p != s.tail { - t.Error("Expected: s.head.n.p == s.tail; Got: s.head.n.p != s.tail") - } - //Check last slice links (not tail anymore; tail is the first one) - if s.head.n.n.n != nil { - t.Error("Expected: s.head.n.n.n == nil; Got: s.head.n.n.n != nil") - } - if s.head.n.n.p != s.tail.n { - t.Error("Expected: s.head.n.n.p == s.tail.n; Got: s.head.n.n.p != s.tail.n") - } + checkLinks(t, s, popValue, maxInternalSliceSize) // Pop maxFirstSliceSize-1 items to empty the head (first) slice for i := 1; i <= maxInternalSliceSize; i++ { @@ -154,21 +113,7 @@ func TestPushPopShouldHaveAllInternalLinksInARing(t *testing.T) { t.Errorf("Expected: %d; Got: %d", popValue, v) } popValue-- - checkLinks(t, s, popValue, maxInternalSliceSize, maxInternalSliceSize, s.tail.n, s.head, s.tail.n, s.head) - /// Check middle links - if s.head.n.n != s.tail.n.n { - t.Error("Expected: s.head.n.n == s.tail.n.n; Got: s.head.n.n != s.tail.n.n") - } - if s.head.n.p != s.tail { - t.Error("Expected: s.head.n.p == s.tail; Got: s.head.n.p != s.tail") - } - //Check last slice links (not tail anymore; tail is the first one) - if s.head.n.n.n != nil { - t.Error("Expected: s.head.n.n.n == nil; Got: s.head.n.n.n != nil") - } - if s.head.n.n.p != s.tail.n { - t.Error("Expected: s.head.n.n.p == s.tail.n; Got: s.head.n.n.p != s.tail.n") - } + checkLinks(t, s, popValue, maxInternalSliceSize) } // The stack shoud be empty @@ -178,40 +123,19 @@ func TestPushPopShouldHaveAllInternalLinksInARing(t *testing.T) { if _, ok := s.Back(); ok { t.Error("Expected: false; Got: true") } - if cap(s.head.v) != maxInternalSliceSize { - t.Errorf("Expected: %d; Got: %d", maxInternalSliceSize, cap(s.head.v)) - } if cap(s.tail.v) != maxInternalSliceSize { t.Errorf("Expected: %d; Got: %d", maxInternalSliceSize, cap(s.tail.v)) } - if s.head.n == s.tail.p { - t.Error("Expected: s.head.n != s.tail.p; Got: s.head.n == s.tail.p") - } } // Helper methods----------------------------------------------------------------------------------- // Checks the internal slices and its links. -func checkLinks(t *testing.T, s *Stack, length, headSliceSize, tailSliceSize int, headNext, headPrevious, tailNext, tailPrevious *node) { +func checkLinks(t *testing.T, s *Stack, length, tailSliceSize int) { t.Helper() if s.Len() != length { t.Errorf("Unexpected length; Expected: %d; Got: %d", length, s.Len()) } - if cap(s.head.v) != headSliceSize { - t.Errorf("Unexpected head size; Expected: %d; Got: %d", headSliceSize, len(s.head.v)) - } - if s.head.n != headNext { - t.Error("Unexpected head node; Expected: s.head.n == headNext; Got: s.head.n != headNext") - } - if s.head.p != headPrevious { - t.Error("Unexpected head; Expected: s.head.p == headPrevious; Got: s.head.p != headPrevious") - } - if s.tail.n != tailNext { - t.Error("Unexpected tailNext; Expected: s.tail.n == tailNext; Got: s.tail.n != tailNext") - } - if s.tail.p != tailPrevious { - t.Error("Unexpected tailPrevious; Expected: s.tail.p == tailPrevious; Got: s.tail.p != tailPrevious") - } if cap(s.tail.v) != tailSliceSize { t.Errorf("Unexpected tail size; Expected: %d; Got: %d", tailSliceSize, len(s.tail.v)) } @@ -231,7 +155,7 @@ func assertInvariants(t *testing.T, s *Stack, val func(i int) interface{}) { if s == nil { fail("non-nil stack", s, "non-nil") } - if s.head == nil { + if s.tail == nil { // Zero value. if s.tail != nil { fail("nil tail when zero", s.tail, nil) @@ -241,85 +165,7 @@ func assertInvariants(t *testing.T, s *Stack, val func(i int) interface{}) { } return } - - spareLinkCount := 0 - inStack := true - elemCount := 0 - smallNodeCount := 0 - index := 0 - walkLinks(t, s, func(n *node) { - if len(n.v) < maxInternalSliceSize { - smallNodeCount++ - if len(n.v) > maxInternalSliceSize { - fail("first node within bounds", len(n.v), maxInternalSliceSize) - } - } - if len(n.v) > maxInternalSliceSize { - fail("slice too big", len(n.v), maxInternalSliceSize) - } - for i, v := range n.v { - failElem := func(what string, got, want interface{}) { - fail(fmt.Sprintf("at elem %d, node %p, %s", i, n, what), got, want) - t.FailNow() - } - if !inStack { - if v != nil { - failElem("all values outside queue nil", v, nil) - } - continue - } - if v != nil { - if val != nil { - want := val(index) - if want != v { - failElem(fmt.Sprintf("element %d has expected value", index), v, want) - } - } - elemCount++ - index++ - } - } - if !inStack { - spareLinkCount++ - } - if n == s.tail { - inStack = false - } - }) - if inStack { - // We never encountered the tail pointer. - t.Errorf("tail does not point to element in list") - } - if elemCount != s.len { - fail("element count == s.len", elemCount, s.len) - } - if smallNodeCount > 1 { - fail("only one first node", smallNodeCount, 1) - } if t.Failed() { t.FailNow() } } - -// walkLinks calls f for each node in the linked list. -// It also checks link invariants: -func walkLinks(t *testing.T, s *Stack, f func(n *node)) { - t.Helper() - fail := func(what string, got, want interface{}) { - t.Errorf("link invariant %s fail; got %v want %v", what, got, want) - } - n := s.head - for { - if n.n != nil && n.n.p != n { - fail("node.n.p == node", n.n.p, n) - } - if n.p.n != nil && n.p.n != n { - fail("node.p.n == node", n.p.n, n) - } - f(n) - n = n.n - if n == nil { - break - } - } -}