Skip to content

Commit 11b25eb

Browse files
Initial commit
1 parent 124a0f1 commit 11b25eb

14 files changed

Lines changed: 1328 additions & 3 deletions

.travis.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
language: go
2+
3+
go:
4+
- "1.10.x"
5+
- "1.11.x"
6+
- tip
7+
8+
before_install:
9+
- go get -t -v ./...
10+
11+
script:
12+
- go test -coverprofile=coverage.txt -covermode=atomic
13+
14+
after_success:
15+
- bash <(curl -s https://codecov.io/bash)

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# 1.0.0
2+
3+
* First stable release, production ready.

CONTRIBUTING.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Contributions
2+
There's a number of ways you can contribute to stack:
3+
4+
- Use it in your projects and let us know your experience
5+
- Let others know about it
6+
- Propose changes by posting [issues](https://github.com/ef-ds/stack/issues)
7+
- Propose changes by creating [PRs](https://github.com/ef-ds/stack/pulls)
8+
- Help with issues labeled ["help wanted"](https://github.com/ef-ds/stack/labels/help%20wanted)
9+
- Support the [proposal](https://github.com/golang/go/issues/27935) to include stack into Go's standard library
10+
11+
If you have suggestions, open issues and do your best to describe your idea. We'll do our best to read and try to understand it. Just make sure to be clear about what you are proposing.
12+
13+
If you feel confident about the proposed change, and the proposed change require small changes to stack's package, feel free to open PRs directly.
14+
15+
We strongly encourage all changes to be benchmarked before sending PRs/merging. To check the impact of the changes to stack, please refer [here](UPDATING_STACK.md).
16+
17+
A big part of the decision whether to accept suggestions and changes through PRs and issues are based on the feedback of the stack users. The rule we use to gather feedback is below:
18+
- If you agree with a suggestion, thumb it up
19+
- If you don't agree with a suggestion, thumb it down
20+
- If you feel strongly about a suggestion, please consider leaving comments
21+
22+
We're 100% committed to below software development rules:
23+
24+
- Correctness
25+
- Performance
26+
- Efficiency
27+
- Simplicity
28+
- Testable code
29+
- Tests, tests, tests!
30+
- Strong test suite covering all code routes/branches
31+
- Strong focus to achieve 100% code coverage everywhere
32+
- Regression tests are a must

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019 Efficient Data Structures
3+
Copyright (c) 2018 ef-ds
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 191 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,191 @@
1-
# stack
2-
Package stack implements a very fast and efficient general purpose stack data structure and is specifically optimized to perform when used by Microservices and serverless services running in production environments.
1+
# 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)
2+
3+
Package stack implements a very fast and efficient general purpose stack data structure and 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.
4+
5+
6+
## Install
7+
From a configured [Go environment](https://golang.org/doc/install#testing):
8+
```sh
9+
go get -u github.com/ef-ds/stack
10+
```
11+
12+
If you are using dep:
13+
```sh
14+
dep ensure -add github.com/ef-ds/stack@1.0.1
15+
```
16+
17+
We recommend to target only released versions for production use.
18+
19+
20+
## How to Use
21+
```go
22+
package main
23+
24+
import (
25+
"fmt"
26+
27+
"github.com/ef-ds/stack"
28+
)
29+
30+
func main() {
31+
var s stack.stack
32+
33+
for i := 1; i <= 5; i++ {
34+
s.Push(i)
35+
}
36+
for s.Len() > 0 {
37+
v, _ := s.Pop()
38+
fmt.Println(v)
39+
}
40+
}
41+
```
42+
43+
Output:
44+
```
45+
1
46+
2
47+
3
48+
4
49+
5
50+
```
51+
52+
Also refer to the [integration](integration_test.go) and [API](api_test.go) tests.
53+
54+
55+
56+
## Tests
57+
Besides having 100% code coverage, stack has an extensive set of [unit](unit_test.go), [integration](integration_test.go) and [API](api_test.go) tests covering all happy, sad and edge cases.
58+
59+
When considering all tests, stack has over 4x more lines of testing code when compared to the actual, functional code.
60+
61+
Performance and efficiency are major concerns, so stack has an extensive set of benchmark tests as well comparing the stack performance with a variety of high quality open source stack implementations.
62+
63+
See the [benchmark tests](https://github.com/ef-ds/stack-bench-tests/blob/master/BENCHMARK_TESTS.md) for details.
64+
65+
66+
## Performance
67+
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 512 (maxInternalSliceSize/sliceGrowthFactor) items and when it expands or grow, it never does so by more than 1024 (maxInternalSliceSize) items in a single operation.
68+
69+
Stack offers either the best or very competitive performance across all test sets, suites and ranges.
70+
71+
As a general purpose LIFO stack, stack offers, by far, the most balanced and consistent performance of all tested data structures.
72+
73+
See [performance](https://github.com/ef-ds/stack-bench-tests/blob/master/PERFORMANCE.md) for details.
74+
75+
76+
## Design
77+
The Efficient Data Structures (ef-ds) stack employs a new, modern stack design: a semi-circular shaped, linked slices design.
78+
79+
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.
80+
81+
![ns/op](testdata/stack.jpg?raw=true "stack Design")
82+
83+
84+
### Design Considerations
85+
Stack uses linked slices as its underlying data structure. The reason for the choice comes from two main observations of slice based stacks/queues/stacks:
86+
87+
1. When the stack needs to expand to accommodate new values, [a new, larger slice needs to be allocated](https://en.wikipedia.org/wiki/Dynamic_array#Geometric_expansion_and_amortized_cost) and used
88+
2. Allocating and managing large slices is expensive, especially in an overloaded system with little available physical memory
89+
90+
To help clarify the scenario, below is what happens when a slice based stack that already holds, say 1bi items, needs to expand to accommodate a new item.
91+
92+
Slice based implementation.
93+
94+
- Allocate a new, twice the size of the previous allocated one, say 2 billion positions slice
95+
- Copy over all 1 billion items from the previous slice into the new one
96+
- Add the new value into the first unused position in the new slice, position 1000000001
97+
98+
The same scenario for stack plays out like below.
99+
100+
- Allocate a new 1024 size slice
101+
- Set the previous and next pointers
102+
- Add the value into the first position of the new slice, position 0
103+
104+
The decision to use linked slices was also the result of the observation that slices goes to great length to provide predictive, indexed positions. A hash table, for instance, absolutely need this property, but not a stack. So stack completely gives up this property and focus on what really matters: add and retrieve from the edges (front/back). No copying around and repositioning of elements is needed for that. So when a slice goes to great length to provide that functionality, the whole work of allocating new arrays, copying data around is all wasted work. None of that is necessary. And this work costs dearly for large data sets as observed in the tests.
105+
106+
While linked slices design solve the slice expansion problem very effectively, it doesn't help with many real world usage scenarios such as in a stable processing environment where small amount of items are pushed and popped from the stack in a sequential way. This is a very common scenario for [Microservices](https://en.wikipedia.org/wiki/Microservices) and [serverless](https://en.wikipedia.org/wiki/Serverless_computing) services, for instance, where the service is able to handle the current traffic without stress.
107+
108+
To address the stable scenario in an effective way, stack keeps its internal linked arrays in a semi-circular shape. This way when items are pushed to the stack after some of them have been removed, the stack will automatically move over its tail slice back to the old head of the stack, effectively reusing the same already allocated slice. The result is a stack that will run through its ring reusing the ring to store the new values, instead of allocating new slices for the new values.
109+
110+
111+
## Supported Data Types
112+
Similarly to Go's standard library list, [list](https://github.com/golang/go/tree/master/src/container/list),
113+
[ring](https://github.com/golang/go/tree/master/src/container/ring) and [heap](https://github.com/golang/go/blob/master/src/container/heap/heap.go) packages, stack supports "interface{}" as its data type. This means it can be used with any Go data types, including int, float, string and any user defined structs and pointers to interfaces.
114+
115+
The data types pushed into the stack can even be mixed, meaning, it's possible to push ints, floats and struct instances into the same stack.
116+
117+
118+
## Safe for Concurrent Use
119+
Stack is not safe for concurrent use. However, it's very easy to build a safe for concurrent use version of the stack. Impl7 design document includes an example of how to make impl7 safe for concurrent use using a mutex. stack can be made safe for concurret use using the same technique. Impl7 design document can be found [here](https://github.com/golang/proposal/blob/master/design/27935-unbounded-queue-package.md).
120+
121+
122+
## Range Support
123+
Just like the current container data structures such as [list](https://github.com/golang/go/tree/master/src/container/list),
124+
[ring](https://github.com/golang/go/tree/master/src/container/ring) and [heap](https://github.com/golang/go/blob/master/src/container/heap/heap.go), stack doesn't support the range keyword for navigation.
125+
126+
However, the API offers two ways to iterate over the stack items. Either use "PopFront"/"PopBack" to retrieve the first current element and the second bool parameter to check for an empty queue.
127+
128+
```go
129+
for v, ok := d.Pop(); ok; v, ok = d.Pop() {
130+
// Do something with v
131+
}
132+
```
133+
134+
Or use "Len" and "Pop" to check for an empty stack and retrieve the first current element.
135+
```go
136+
for d.Len() > 0 {
137+
v, _ := d.Pop()
138+
// Do something with v
139+
}
140+
```
141+
142+
143+
144+
## Why
145+
We feel like this world needs improving. Our goal is to change the world, for the better, for everyone.
146+
147+
As software engineers at ef-ds, we feel like the best way we can contribute to a better world is to build amazing systems,
148+
systems that solve real world problems, with unheard performance and efficiency.
149+
150+
We believe in challenging the status-quo. We believe in thinking differently. We believe in progress.
151+
152+
What if we could build queues, stacks, lists, arrays, hash tables, etc that are much faster than the current ones we have? What if we had a dynamic array data structure that offers near constant time deletion (anywhere in the array)? Or that could handle 1 million items data sets using only 1/3 of the memory when compared to all known current implementations? And still runs 2x as fast?
153+
154+
One sofware engineer can't change the world him/herself, but a whole bunch of us can! Please join us improving this world. All the work done here is made 100% transparent and is 100% free. No strings attached. We only require one thing in return: please consider benefiting from it; and if you do so, please let others know about it.
155+
156+
157+
## Competition
158+
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.
159+
160+
161+
## Releases
162+
We're committed to a CI/CD lifecycle releasing frequent, but only stable, production ready versions with all proper tests in place.
163+
164+
We strive as much as possible to keep backwards compatibility with previous versions, so breaking changes are a no-go.
165+
166+
For a list of changes in each released version, see [CHANGELOG.md](CHANGELOG.md).
167+
168+
169+
## Supported Go Versions
170+
See [supported_go_versions.md](https://github.com/ef-ds/docs/blob/master/supported_go_versions.md).
171+
172+
173+
## License
174+
MIT, see [LICENSE](LICENSE).
175+
176+
"Use, abuse, have fun and contribute back!"
177+
178+
179+
## Contributions
180+
See [CONTRIBUTING.md](CONTRIBUTING.md).
181+
182+
183+
## Roadmap
184+
- Build tool to help find out the combination of firstSliceSize, sliceGrowthFactor and maxInternalSliceSize that will yield the best performance
185+
- Find the fastest open source stacks and add them the bench tests
186+
- Improve stack performance and/or efficiency by improving its design and/or implementation
187+
- Build a high performance safe for concurrent use version of stack
188+
189+
190+
## Contact
191+
Suggestions, bugs, new queues to benchmark, issues with the stack, please let us know at ef-ds@outlook.com.

UPDATING_STACK.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
## Updating Stack and Checking the Results
2+
3+
If you want to make changes to stack and run the tests to check the effect on performance and memory,
4+
we suggest you run all the benchmark tests locally once using below command.
5+
6+
```
7+
go test -benchmem -count 10 -timeout 60m -bench="stack*" -run=^$ > testdata/stack.txt
8+
```
9+
10+
Then make the changes and re-run the tests using below command (notice the output file now is stack2.txt).
11+
12+
```
13+
go test -benchmem -count 10 -timeout 60m -bench="stack*" -run=^$ > testdata/stack2.txt
14+
```
15+
16+
Then run the [test-splitter](https://github.com/ef-ds/tools/tree/master/testsplitter) tool as follow:
17+
18+
```
19+
go run *.go --file PATH_TO_TESTDATA/stack2.txt --suffix 2
20+
```
21+
22+
Test-splitter should create each file with the "2" suffix, so now we have the test file for both, the old and this new
23+
test run. Use below commands to test the effect of the changes for each test suite.
24+
25+
```
26+
benchstat testdata/BenchmarkMicroservice.txt testdata/BenchmarkMicroservice2.txt
27+
benchstat testdata/BenchmarkFill.txt testdata/BenchmarkFill2.txt
28+
benchstat testdata/BenchmarkRefill.txt testdata/BenchmarkRefill2.txt
29+
benchstat testdata/BenchmarkRefillFull.txt testdata/BenchmarkRefillFull2.txt
30+
benchstat testdata/BenchmarkSlowIncrease.txt testdata/BenchmarkSlowIncrease2.txt
31+
benchstat testdata/BenchmarkSlowIncrease.txt testdata/BenchmarkSlowIncrease2.txt
32+
benchstat testdata/BenchmarkStable.txt testdata/BenchmarkStable2.txt
33+
```

0 commit comments

Comments
 (0)