├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── MIGRATE_V1_TO_V2.md ├── README.md ├── UPDATING_DEQUE.md ├── api_test.go ├── benchmark_test.go ├── deque.go ├── doc_test.go ├── go.mod ├── integration_test.go ├── testdata ├── deque.jpg ├── release_v1.0.1.md ├── release_v1.0.2.md ├── release_v1.0.3.md └── release_v2.0.0.md └── unit_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - "1.10.x" 5 | - "1.11.x" 6 | - "1.12.x" 7 | - "1.13.x" 8 | - "1.14.x" 9 | - "1.15.x" 10 | - "1.16.x" 11 | - "1.17.x" 12 | - "1.18.x" 13 | - "1.19.x" 14 | - "1.20.x" 15 | - tip 16 | 17 | before_install: 18 | - go get -t -v ./... 19 | 20 | script: 21 | - go test -coverprofile=coverage.txt -covermode=atomic 22 | 23 | after_success: 24 | - bash <(curl -s https://codecov.io/bash) 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.0.0 2 | 3 | * First stable release, production ready, certified to use as a FIFO queue or LIFO stack. Mixed Push/Pop/Front/Back is pending more testing and so is not recommended for use in a production setting. 4 | 5 | 6 | # 1.0.1 7 | 8 | * Fixed bug related to spare slices. The bug where the deque was not eliminating reused slices correctly caused it to cache more slices than maxSpareLinks (4), inflating memory unnecessarily. 9 | - Commit 1: https://github.com/ef-ds/deque/commit/5cda9cbd756b5001cd8bb5e2b33675d65c61149d 10 | - Commit 2: https://github.com/ef-ds/deque/commit/4de25e4de16dfe904669fe0a4ad3b0d189095fad 11 | - Benchmark tests: [v1.0.0 vs v1.0.1](testdata/release_v1.0.1.md) 12 | 13 | * Mixed Push/Pop/Front/Back is pending more testing and so is not recommended for use in production environment. 14 | 15 | 16 | # 1.0.2 17 | 18 | * Many improvements to make the code and the tests more readable and easier to maintain; the deque is also faster and uses less memory now. Amazing job, [Roger](https://github.com/rogpeppe)! 19 | - Benchmark tests: [v1.0.1 vs v1.0.2](testdata/release_v1.0.2.md) 20 | 21 | * Mixed Push/Pop/Front/Back is pending more testing and so is not recommended for use in production environment. 22 | 23 | 24 | # 1.0.3 25 | - Optimized deque: [here](https://github.com/ef-ds/deque/pull/13) and [here](https://github.com/ef-ds/deque/pull/14) 26 | - Improved mixed tests: [here](https://github.com/ef-ds/deque/pull/15) 27 | - Moved comparison benchmark tests to separate repo: [here](https://github.com/ef-ds/deque/pull/16) 28 | - Benchmark tests: [v1.0.2 vs v1.0.3](testdata/release_v1.0.3.md); [comparison tests](https://github.com/ef-ds/deque-bench-tests/blob/master/PERFORMANCE.md) 29 | - Mixed Push/Pop/Front/Back is now fully tested and should be fine to be used in production environments 30 | 31 | # 1.0.4 32 | - Fixed bug related to PushFront/PopBack: [here](https://github.com/ef-ds/deque/pull/21) 33 | - The minor change had no significant performance impact 34 | 35 | # 2.0.0 36 | - Updated Deque to support generics! 37 | - This is a breaking change and will require users to specify the desired data type used with Deque (such as Deque[int]). We recommend the users to update their existing code to use the specific data type the Deque needs to handle (such as int, string, etc), but the interface type can still be used. Just declare Deque as "var d deque.Deque[interface{}]" and use it normally. No other changes should be required to use Deque v2.0.0. 38 | - Deque v2 is up to 10% faster and uses up to 35% less memory it used to use in most test cases! Benchmark tests: [v1.0.3 vs v2.0.0](testdata/release_v2.0.0.md); [comparison tests](https://github.com/ef-ds/deque-bench-tests/blob/master/PERFORMANCE.md) 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributions 2 | There's a number of ways you can contribute to deque: 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/deque/issues) 7 | - Propose changes by creating [PRs](https://github.com/ef-ds/deque/pulls) 8 | - Help with issues labeled ["help wanted"](https://github.com/ef-ds/deque/labels/help%20wanted) 9 | - Support the [proposal](https://github.com/golang/go/issues/27935) to include deque 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 deque'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 deque, please refer [here](UPDATING_DEQUE.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 deque 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 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 ef-ds 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MIGRATE_V1_TO_V2.md: -------------------------------------------------------------------------------- 1 | # How to Migrate from Deque V1 to V2 2 | 3 | After have downloaded Deque v2 version into your project (`go get -u github.com/ef-ds/deque/v2`), below code changes are required in order to migrate to v2. 4 | 5 | - Update the imports path from `github.com/ef-ds/deque` to `deque github.com/ef-ds/deque/v2` 6 | - Update the Deque's declaration from `var d deque.Deque` to `var d deque.Deque[data_type]` and/or `d := deque.New()` to `d := deque.New[data_type]()` 7 | - Remove any `interface{}` type casts (i.e. `var v, _ := d.Pop(); typeCast := v.(data_type)`) 8 | 9 | Below are examples of migrated code: 10 | 11 | Simple int as data type - V1 version: 12 | ```go 13 | import "github.com/ef-ds/deque" 14 | 15 | var d deque.Deque 16 | for i := 1; i <= 5; i++ { 17 | d.PushBack(i) 18 | } 19 | 20 | for d.Len() > 0 { 21 | v, _ := d.PopFront() 22 | intV := v.(int) 23 | fmt.Print(intV) 24 | } 25 | ``` 26 | 27 | Simple int as data type - V2 version: 28 | ```go 29 | import deque "github.com/ef-ds/deque/v2" 30 | 31 | var d deque.Deque[int] 32 | for i := 1; i <= 5; i++ { 33 | d.PushBack(i) 34 | } 35 | 36 | for d.Len() > 0 { 37 | intV, _ := d.PopFront() 38 | fmt.Print(intV) 39 | } 40 | ``` 41 | 42 | Custom struct as data type - V1 version: 43 | ```go 44 | import "github.com/ef-ds/deque" 45 | 46 | type myType struct { 47 | value int 48 | } 49 | 50 | d := deque.New() 51 | for i := 1; i <= 5; i++ { 52 | d.PushBack(&myType{value: i}) 53 | } 54 | 55 | for d.Len() > 0 { 56 | v, _ := d.PopFront() 57 | myTypeV := v.(*myType) 58 | fmt.Print(myTypeV.value) 59 | } 60 | ``` 61 | 62 | Custom struct as data type - V2 version: 63 | ```go 64 | import deque "github.com/ef-ds/deque/v2" 65 | 66 | type myType struct { 67 | value int 68 | } 69 | 70 | d := deque.New[*myType]() 71 | for i := 1; i <= 15; i++ { 72 | d.PushBack(&myType{value: i}) 73 | } 74 | 75 | for d.Len() > 0 { 76 | v, _ := d.PopFront() 77 | fmt.Print(v.value) 78 | } 79 | ``` 80 | 81 | Mixed data types used as data type - V1 version: 82 | ```go 83 | import "github.com/ef-ds/deque" 84 | 85 | d := deque.New() 86 | for i := 1; i <= 5; i++ { 87 | if i%2 == 0 { 88 | // Push value as int 89 | d.PushBack(i) 90 | } else { 91 | // Push value as string 92 | d.PushBack(strconv.Itoa(i)) 93 | } 94 | } 95 | 96 | for d.Len() > 0 { 97 | v, _ := d.PopFront() 98 | switch t := v.(type) { 99 | case int: 100 | fmt.Printf("Int value: %d", t) 101 | case string: 102 | fmt.Printf("String value: %s", t) 103 | default: 104 | fmt.Print("Unrecognized type") 105 | } 106 | } 107 | ``` 108 | 109 | Mixed data types used as data type - V2 version: 110 | ```go 111 | import deque "github.com/ef-ds/deque/v2" 112 | 113 | d := deque.New[interface{}]() 114 | for i := 1; i <= 5; i++ { 115 | if i%2 == 0 { 116 | // Push value as int 117 | d.PushBack(i) 118 | } else { 119 | // Push value as string 120 | d.PushBack(strconv.Itoa(i)) 121 | } 122 | } 123 | 124 | for d.Len() > 0 { 125 | v, _ := d.PopFront() 126 | switch t := v.(type) { 127 | case int: 128 | fmt.Printf("Int value: %d", t) 129 | case string: 130 | fmt.Printf("String value: %s", t) 131 | default: 132 | fmt.Print("Unrecognized type") 133 | } 134 | } 135 | ``` 136 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # deque [![Build Status](https://travis-ci.com/ef-ds/deque.svg?branch=master)](https://travis-ci.com/ef-ds/deque) [![codecov](https://codecov.io/gh/ef-ds/deque/branch/master/graph/badge.svg)](https://codecov.io/gh/ef-ds/deque) [![Go Report Card](https://goreportcard.com/badge/github.com/ef-ds/deque)](https://goreportcard.com/report/github.com/ef-ds/deque) [![GoDoc](https://godoc.org/github.com/ef-ds/deque?status.svg)](https://godoc.org/github.com/ef-ds/deque) 2 | 3 | Package deque implements a very fast and efficient general purpose queue/stack/deque data structure that is specifically optimized to perform when used by Microservices and serverless services running in production environments. Internally, deque stores the elements in a dynamically growing 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/deque/v2 10 | ``` 11 | 12 | We recommend to target only released versions for production use. 13 | 14 | 15 | ## How to Use 16 | As a First-In-First-Out [queue](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)). 17 | 18 | ```go 19 | package main 20 | 21 | import ( 22 | "fmt" 23 | 24 | deque "github.com/ef-ds/deque/v2" 25 | ) 26 | 27 | func main() { 28 | var d deque.Deque[int] 29 | 30 | for i := 1; i <= 5; i++ { 31 | d.PushBack(i) 32 | } 33 | for d.Len() > 0 { 34 | v, _ := d.PopFront() 35 | fmt.Println(v) 36 | } 37 | } 38 | ``` 39 | 40 | Output: 41 | ``` 42 | 1 43 | 2 44 | 3 45 | 4 46 | 5 47 | ``` 48 | 49 | 50 | As a Last-In-First-Out [stack](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)). 51 | 52 | ```go 53 | package main 54 | 55 | import ( 56 | "fmt" 57 | 58 | deque "github.com/ef-ds/deque/v2" 59 | ) 60 | 61 | func main() { 62 | var d deque.Deque[int] 63 | 64 | for i := 1; i <= 5; i++ { 65 | d.PushBack(i) 66 | } 67 | for d.Len() > 0 { 68 | v, _ := d.PopBack() 69 | fmt.Println(v) 70 | } 71 | } 72 | ``` 73 | 74 | Output: 75 | ``` 76 | 5 77 | 4 78 | 3 79 | 2 80 | 1 81 | ``` 82 | 83 | Also refer to the [integration](integration_test.go) and [API](api_test.go) tests. 84 | 85 | ## Generics 86 | 87 | Starting with v2.0.0, Deque supports generics. Looking for a previous non-generic stable release? Check out version [1.0.4](https://github.com/ef-ds/deque/releases/tag/v1.0.4). 88 | 89 | Looking to migrate from v1 to v2? Check [this](MIGRATE_V1_TO_V2.md) out! 90 | 91 | 92 | ## Tests 93 | Besides having 100% code coverage, deque 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. 94 | 95 | Performance and efficiency are major concerns, so deque has an extensive set of benchmark tests as well comparing the deque performance with a variety of high quality open source deque implementations. 96 | 97 | See the [benchmark tests](https://github.com/ef-ds/deque-bench-tests/blob/master/BENCHMARK_TESTS.md) for details. 98 | 99 | When considering all tests, deque has over 10x more lines of testing code when compared to the actual, functional code. 100 | 101 | 102 | ## Performance 103 | Deque has constant time (O(1)) on all its operations (PushFront/PushBack/PopFront/PopBack/Len). It's not amortized constant as it is with most [traditional growing array deques](https://en.wikipedia.org/wiki/Double-ended_queue#Complexity) because deque never copies more than 16 (maxFirstSliceSize/sliceGrowthFactor) items and when it expands or grow, it never does so by more than 256 (maxInternalSliceSize) items in a single operation. 104 | 105 | Deque, either used as a FIFO queue or LIFO stack, offers either the best or very competitive performance across all test sets, suites and ranges. 106 | 107 | As a general purpose FIFO deque or LIFO stack, deque offers, by far, the most balanced and consistent performance of all tested data structures. 108 | 109 | See [performance](https://github.com/ef-ds/deque-bench-tests/blob/master/PERFORMANCE.md) for details. 110 | 111 | 112 | ## Design 113 | The Efficient Data Structures (ef-ds) deque employs a new, modern deque design: a ring shaped, auto shrinking, linked slices design. 114 | 115 | That means the [double-ended queue](https://en.wikipedia.org/wiki/Double-ended_queue) 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 ring in shape because the linked list is a [circular one](https://en.wikipedia.org/wiki/Circular_buffer), where the last node always points to the first one in the ring. It also auto shrinks as the values are popped off the deque, keeping itself as a lean and low memory footprint data structure even after heavy use. 116 | 117 | ![ns/op](testdata/deque.jpg?raw=true "Deque Design") 118 | 119 | 120 | ### Design Considerations 121 | Deque uses linked slices as its underlying data structure. The reason for the choice comes from two main observations of slice based deques/queues/stacks: 122 | 123 | 1. When the deque/queue/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 124 | 2. Allocating and managing large slices is expensive, especially in an overloaded system with little available physical memory 125 | 126 | To help clarify the scenario, below is what happens when a slice based deque that already holds, say 1bi items, needs to expand to accommodate a new item. 127 | 128 | Slice based implementation. 129 | 130 | - Allocate a new, twice the size of the previous allocated one, say 2 billion positions slice 131 | - Copy over all 1 billion items from the previous slice into the new one 132 | - Add the new value into the first unused position in the new slice, position 1000000001 133 | 134 | The same scenario for deque plays out like below. 135 | 136 | - Allocate a new 256 size slice 137 | - Set the previous and next pointers 138 | - Add the value into the first position of the new slice, position 0 139 | 140 | 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 deque. So deque 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. 141 | 142 | 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 deque 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. 143 | 144 | To address the stable scenario in an effective way, deque keeps its internal linked arrays in a circular, ring shape. This way when items are pushed to the deque after some of them have been removed, the deque will automatically move over its tail slice back to the old head of the deque, effectively reusing the same already allocated slice. The result is a deque that will run through its ring reusing the ring to store the new values, instead of allocating new slices for the new values. 145 | 146 | Another important real world scenario is when a service is subject to high and low traffic patterns over time. It is desired the service to be able to scale out quickly and effectively to handle the extra traffic when needed, but also to scale in, decreasing resource usage, such as CPU and memory, when the traffic subsides, so the cost to run the system over time is optimized. 147 | 148 | To solve this problem, deque employs an automatically shrinking mechanism that will shrink, releasing the extra resources from memory, as the number of items in the deque decreases. This mechanism allows the queue to shrink when needed, keeping itself lean, but also solves a major problem of most ring based implementations: once inflated, the data structure either never shrinks or require manual "shrink" calls, which can be tricky to use as the ideal, most optimized moment to shrink is not always clear and well defined. 149 | 150 | Having said that, a data structure that completely shrinks after use, when it is used again, it means it has to expand again to accommodate the new values, hindering performance on refill scenarios (where a number of items is added and removed from the deque successively). To address this scenario, deque keeps a configurable number of internal, empty, slices in its ring. This way in refill scenarios deque is able to scale out very quickly, but still managing to keep the memory footprint very low. 151 | 152 | 153 | 154 | ## Supported Data Types 155 | Similarly to Go's standard library list, [list](https://github.com/golang/go/tree/master/src/container/list), 156 | [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, deque 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. 157 | 158 | The data types pushed into the deque can even be mixed, meaning, it's possible to push ints, floats and struct instances into the same deque. 159 | 160 | 161 | ## Safe for Concurrent Use 162 | Deque is not safe for concurrent use. However, it's very easy to build a safe for concurrent use version of the deque. Impl7 design document includes an example of how to make impl7 safe for concurrent use using a mutex. Deque 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). 163 | 164 | 165 | ## Range Support 166 | Just like the current container data structures such as [list](https://github.com/golang/go/tree/master/src/container/list), 167 | [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), deque doesn't support the range keyword for navigation. 168 | 169 | However, the API offers two ways to iterate over the deque items. Either use "PopFront"/"PopBack" to retrieve the first current element and the second bool parameter to check for an empty queue. 170 | 171 | ```go 172 | for v, ok := d.PopFront(); ok; v, ok = d.PopFront() { 173 | // Do something with v 174 | } 175 | ``` 176 | 177 | Or use "Len" and "PopFront"/"PopBack" to check for an empty deque and retrieve the first current element. 178 | ```go 179 | for d.Len() > 0 { 180 | v, _ := d.PopFront() 181 | // Do something with v 182 | } 183 | ``` 184 | 185 | 186 | 187 | ## Why 188 | We feel like this world needs improving. Our goal is to change the world, for the better, for everyone. 189 | 190 | As software engineers at ef-ds, we feel like the best way we can contribute to a better world is to build amazing systems, 191 | systems that solve real world problems, with unheard performance and efficiency. 192 | 193 | We believe in challenging the status-quo. We believe in thinking differently. We believe in progress. 194 | 195 | 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? 196 | 197 | 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. 198 | 199 | 200 | ## Support 201 | As the [CloudLogger](https://github.com/cloud-logger/docs) project needed a high performance unbounded queue and, given the fact that Go doesn't provide such queue in its standard library, we built a new queue and proposed it to be added to the standard library. 202 | 203 | The initial [proposal](https://github.com/golang/go/issues/27935) was to add [impl7](https://github.com/christianrpetrin/queue-tests/tree/master/queueimpl7/queueimpl7.go) to the standard library. 204 | 205 | Given the suggestions to build a deque instead of a FIFO queue as a deque is a much more flexible data structure, coupled with suggestions to build a proper external package, this deque package was built. 206 | 207 | We truly believe in the deque and we believe it should have a place in the Go's standard library, so all Go users can benefit from this data structure, and not only the Go insider ones or the lucky ones who found out about the deque by chance or were lucky enough to find it through the search engines. 208 | 209 | If you like deque, please help us support it by thumbing up the [proposal](https://github.com/golang/go/issues/27935) and leaving comments. 210 | 211 | 212 | ## Competition 213 | We're extremely interested in improving deque 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 queues not tested here, let us know and we're very glad to benchmark them. 214 | 215 | 216 | ## Releases 217 | We're committed to a CI/CD lifecycle releasing frequent, but only stable, production ready versions with all proper tests in place. 218 | 219 | We strive as much as possible to keep backwards compatibility with previous versions, so breaking changes are a no-go. 220 | 221 | For a list of changes in each released version, see [CHANGELOG.md](CHANGELOG.md). 222 | 223 | 224 | ## Supported Go Versions 225 | See [supported_go_versions.md](https://github.com/ef-ds/docs/blob/master/supported_go_versions.md). 226 | 227 | 228 | ## License 229 | MIT, see [LICENSE](LICENSE). 230 | 231 | "Use, abuse, have fun and contribute back!" 232 | 233 | 234 | ## Contributions 235 | See [CONTRIBUTING.md](CONTRIBUTING.md). 236 | 237 | 238 | ## Roadmap 239 | - Build tool to help find out the combination of firstSliceSize, sliceGrowthFactor, maxFirstSliceSize, maxInternalSliceSize and maxSpareLinks that will yield the best performance 240 | - Build tool to automatically run and calculate the final score of all tests 241 | - Find the fastest open source deques and add them the bench tests 242 | - Improve deque performance and/or efficiency by improving its design and/or implementation 243 | - Build a high performance safe for concurrent use version of deque 244 | 245 | 246 | ## Contact 247 | Suggestions, bugs, new queues to benchmark, issues with the deque, please let us know at ef-ds@outlook.com. 248 | -------------------------------------------------------------------------------- /UPDATING_DEQUE.md: -------------------------------------------------------------------------------- 1 | ## Updating Deque and Checking the Results 2 | 3 | If you want to make changes to deque 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="Deque*" -run=^$ > testdata/deque.txt 8 | ``` 9 | 10 | Then make the changes and re-run the tests using below command (notice the output file now is deque2.txt). 11 | 12 | ``` 13 | go test -benchmem -count 10 -timeout 60m -bench="Deque*" -run=^$ > testdata/deque2.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/deque2.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/BenchmarkMicroserviceQueue.txt testdata/BenchmarkMicroserviceQueue2.txt 27 | benchstat testdata/BenchmarkMicroserviceStack.txt testdata/BenchmarkMicroserviceStack2.txt 28 | benchstat testdata/BenchmarkFillQueue.txt testdata/BenchmarkFillQueue2.txt 29 | benchstat testdata/BenchmarkFillStack.txt testdata/BenchmarkFillStack2.txt 30 | benchstat testdata/BenchmarkRefillQueue.txt testdata/BenchmarkRefillQueue2.txt 31 | benchstat testdata/BenchmarkRefillStack.txt testdata/BenchmarkRefillStack2.txt 32 | benchstat testdata/BenchmarkRefillFullQueue.txt testdata/BenchmarkRefillFullQueue2.txt 33 | benchstat testdata/BenchmarkRefillFullStack.txt testdata/BenchmarkRefillFullStack2.txt 34 | benchstat testdata/BenchmarkSlowIncreaseQueue.txt testdata/BenchmarkSlowIncreaseQueue2.txt 35 | benchstat testdata/BenchmarkSlowIncreaseStack.txt testdata/BenchmarkSlowIncreaseStack2.txt 36 | benchstat testdata/BenchmarkSlowDecreaseQueue.txt testdata/BenchmarkSlowDecreaseQueue2.txt 37 | benchstat testdata/BenchmarkSlowIncreaseStack.txt testdata/BenchmarkSlowIncreaseStack2.txt 38 | benchstat testdata/BenchmarkStableQueue.txt testdata/BenchmarkStableQueue2.txt 39 | benchstat testdata/BenchmarkStableStack.txt testdata/BenchmarkStableStack2.txt 40 | ``` 41 | -------------------------------------------------------------------------------- /api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ef-ds 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | package deque_test 22 | 23 | import ( 24 | "testing" 25 | 26 | deque "github.com/ef-ds/deque/v2" 27 | ) 28 | 29 | func TestPopFrontWithZeroValueShouldReturnReadyToUseDeque(t *testing.T) { 30 | var d deque.Deque[int] 31 | d.PushBack(1) 32 | d.PushBack(2) 33 | 34 | v, ok := d.PopFront() 35 | if !ok || v != 1 { 36 | t.Errorf("Expected: 1; Got: %d", v) 37 | } 38 | v, ok = d.PopFront() 39 | if !ok || v != 2 { 40 | t.Errorf("Expected: 2; Got: %d", v) 41 | } 42 | _, ok = d.PopFront() 43 | if ok { 44 | t.Error("Expected: empty slice (ok=false); Got: ok=true") 45 | } 46 | } 47 | 48 | func TestPopBackWithZeroValueShouldReturnReadyToUseDeque(t *testing.T) { 49 | var d deque.Deque[int] 50 | d.PushBack(1) 51 | d.PushBack(2) 52 | 53 | v, ok := d.PopBack() 54 | if !ok || v != 2 { 55 | t.Errorf("Expected: 2; Got: %d", v) 56 | } 57 | v, ok = d.PopBack() 58 | if !ok || v != 1 { 59 | t.Errorf("Expected: 1; Got: %d", v) 60 | } 61 | _, ok = d.PopBack() 62 | if ok { 63 | t.Error("Expected: empty slice (ok=false); Got: ok=true") 64 | } 65 | } 66 | 67 | func TestWithZeroValueAndEmptyShouldReturnAsEmpty(t *testing.T) { 68 | var d deque.Deque[int] 69 | if _, ok := d.Front(); ok { 70 | t.Error("Expected: false as the queue is empty; Got: true") 71 | } 72 | if _, ok := d.Back(); ok { 73 | t.Error("Expected: false as the queue is empty; Got: true") 74 | } 75 | if _, ok := d.PopFront(); ok { 76 | t.Error("Expected: false as the queue is empty; Got: true") 77 | } 78 | if _, ok := d.PopBack(); ok { 79 | t.Error("Expected: false as the queue is empty; Got: true") 80 | } 81 | if l := d.Len(); l != 0 { 82 | t.Errorf("Expected: 0 as the queue is empty; Got: %d", l) 83 | } 84 | } 85 | 86 | func TestInitShouldReturnEmptyDeque(t *testing.T) { 87 | var d deque.Deque[int] 88 | d.PushBack(1) 89 | 90 | d.Init() 91 | 92 | if _, ok := d.Front(); ok { 93 | t.Error("Expected: false as the queue is empty; Got: true") 94 | } 95 | if _, ok := d.Back(); ok { 96 | t.Error("Expected: false as the queue is empty; Got: true") 97 | } 98 | if _, ok := d.PopFront(); ok { 99 | t.Error("Expected: false as the queue is empty; Got: true") 100 | } 101 | if _, ok := d.PopBack(); ok { 102 | t.Error("Expected: false as the queue is empty; Got: true") 103 | } 104 | if l := d.Len(); l != 0 { 105 | t.Errorf("Expected: 0 as the queue is empty; Got: %d", l) 106 | } 107 | } 108 | 109 | func TestPopFrontWithNilValuesShouldReturnAllValuesInOrder(t *testing.T) { 110 | d := deque.New[interface{}]() 111 | d.PushBack(1) 112 | d.PushBack(nil) 113 | d.PushBack(2) 114 | d.PushBack(nil) 115 | 116 | v, ok := d.PopFront() 117 | if !ok || v.(int) != 1 { 118 | t.Errorf("Expected: 1; Got: %d", v) 119 | } 120 | v, ok = d.PopFront() 121 | if !ok || v != nil { 122 | t.Errorf("Expected: 1; Got: %d", v) 123 | } 124 | v, ok = d.PopFront() 125 | if !ok || v.(int) != 2 { 126 | t.Errorf("Expected: 1; Got: %d", v) 127 | } 128 | v, ok = d.PopFront() 129 | if !ok || v != nil { 130 | t.Errorf("Expected: 1; Got: %d", v) 131 | } 132 | _, ok = d.PopFront() 133 | if ok { 134 | t.Error("Expected: empty slice (ok=false); Got: ok=true") 135 | } 136 | } 137 | 138 | func TestPopBackWithNilValuesShouldReturnAllValuesInOrder(t *testing.T) { 139 | d := deque.New[interface{}]() 140 | d.PushBack(1) 141 | d.PushBack(nil) 142 | d.PushBack(2) 143 | d.PushBack(nil) 144 | 145 | v, ok := d.PopBack() 146 | if !ok || v != nil { 147 | t.Errorf("Expected: nil; Got: %d", v) 148 | } 149 | v, ok = d.PopBack() 150 | if !ok || v.(int) != 2 { 151 | t.Errorf("Expected: 2; Got: %d", v) 152 | } 153 | v, ok = d.PopBack() 154 | if !ok || v != nil { 155 | t.Errorf("Expected: nil; Got: %d", v) 156 | } 157 | v, ok = d.PopBack() 158 | if !ok || v.(int) != 1 { 159 | t.Errorf("Expected: 1; Got: %d", v) 160 | } 161 | _, ok = d.PopBack() 162 | if ok { 163 | t.Error("Expected: empty slice (ok=false); Got: ok=true") 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /benchmark_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ef-ds 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | // 21 | // Below tests are used mostly for comparing the result of changes to deque and 22 | // are not necessarily a replication of the comparison tests. For instance, 23 | // the stack tests here use PushFront/PopFront instead of PushBack/PopBack. 24 | // 25 | // For comparing deque performance with other deques, refer to 26 | // https://github.com/ef-ds/deque-bench-tests 27 | 28 | package deque_test 29 | 30 | import ( 31 | "strconv" 32 | "testing" 33 | 34 | deque "github.com/ef-ds/deque/v2" 35 | ) 36 | 37 | // testData contains the number of items to add to the queues in each test. 38 | type testData struct { 39 | count int 40 | } 41 | 42 | var ( 43 | tests = []testData{ 44 | {count: 0}, 45 | {count: 1}, 46 | {count: 10}, 47 | {count: 100}, 48 | {count: 1000}, // 1k 49 | {count: 10000}, //10k 50 | {count: 100000}, // 100k 51 | {count: 1000000}, // 1mi 52 | } 53 | 54 | // Used to store temp values, avoiding any compiler optimizations. 55 | tmp *testData 56 | tmp2 bool 57 | 58 | fillCount = 10000 59 | refillCount = 100 60 | ) 61 | 62 | func BenchmarkFillQueue(b *testing.B) { 63 | for _, test := range tests { 64 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 65 | for n := 0; n < b.N; n++ { 66 | q := deque.New[*testData]() 67 | for i := 0; i < test.count; i++ { 68 | q.PushBack(nil) 69 | } 70 | for q.Len() > 0 { 71 | tmp, tmp2 = q.PopFront() 72 | } 73 | } 74 | }) 75 | } 76 | } 77 | 78 | func BenchmarkFillStack(b *testing.B) { 79 | for _, test := range tests { 80 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 81 | for n := 0; n < b.N; n++ { 82 | q := deque.New[*testData]() 83 | for i := 0; i < test.count; i++ { 84 | q.PushFront(nil) 85 | } 86 | for q.Len() > 0 { 87 | tmp, tmp2 = q.PopFront() 88 | } 89 | } 90 | }) 91 | } 92 | } 93 | 94 | func BenchmarkRefillQueue(b *testing.B) { 95 | for i, test := range tests { 96 | // Doesn't run the first (0 items) and last (1mi) items tests 97 | // as 0 items makes no sense for this test and 1mi is too slow. 98 | if i == 0 || i > 6 { 99 | continue 100 | } 101 | 102 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 103 | q := deque.New[*testData]() 104 | for n := 0; n < b.N; n++ { 105 | for n := 0; n < refillCount; n++ { 106 | for i := 0; i < test.count; i++ { 107 | q.PushBack(nil) 108 | } 109 | for q.Len() > 0 { 110 | tmp, tmp2 = q.PopFront() 111 | } 112 | } 113 | } 114 | }) 115 | } 116 | } 117 | 118 | func BenchmarkRefillStack(b *testing.B) { 119 | for _, test := range tests { 120 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 121 | q := deque.New[*testData]() 122 | for n := 0; n < b.N; n++ { 123 | for n := 0; n < refillCount; n++ { 124 | for i := 0; i < test.count; i++ { 125 | q.PushFront(nil) 126 | } 127 | for q.Len() > 0 { 128 | tmp, tmp2 = q.PopFront() 129 | } 130 | } 131 | } 132 | }) 133 | } 134 | } 135 | 136 | func BenchmarkRefillFullQueue(b *testing.B) { 137 | d := deque.New[*testData]() 138 | for i := 0; i < fillCount; i++ { 139 | d.PushBack(nil) 140 | } 141 | 142 | for i, test := range tests { 143 | // Doesn't run the first (0 items) and last two (100k, 1mi) items tests 144 | // as 0 items makes no sense for this test and 1mi is too slow. 145 | if i == 0 || i > 5 { 146 | continue 147 | } 148 | 149 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 150 | for n := 0; n < b.N; n++ { 151 | for k := 0; k < refillCount; k++ { 152 | for i := 0; i < test.count; i++ { 153 | d.PushBack(nil) 154 | } 155 | for i := 0; i < test.count; i++ { 156 | tmp, tmp2 = d.PopFront() 157 | } 158 | } 159 | } 160 | }) 161 | } 162 | 163 | for d.Len() > 0 { 164 | tmp, tmp2 = d.PopFront() 165 | } 166 | } 167 | 168 | func BenchmarkRefillFullStack(b *testing.B) { 169 | d := deque.New[*testData]() 170 | for i := 0; i < fillCount; i++ { 171 | d.PushBack(nil) 172 | } 173 | 174 | for _, test := range tests { 175 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 176 | for n := 0; n < b.N; n++ { 177 | for k := 0; k < refillCount; k++ { 178 | for i := 0; i < test.count; i++ { 179 | d.PushFront(nil) 180 | } 181 | for i := 0; i < test.count; i++ { 182 | tmp, tmp2 = d.PopFront() 183 | } 184 | } 185 | } 186 | }) 187 | } 188 | 189 | for d.Len() > 0 { 190 | tmp, tmp2 = d.PopBack() 191 | } 192 | } 193 | 194 | func BenchmarkStableQueue(b *testing.B) { 195 | d := deque.New[*testData]() 196 | for i := 0; i < fillCount; i++ { 197 | d.PushBack(nil) 198 | } 199 | 200 | for _, test := range tests { 201 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 202 | for n := 0; n < b.N; n++ { 203 | for i := 0; i < test.count; i++ { 204 | d.PushBack(nil) 205 | tmp, tmp2 = d.PopFront() 206 | } 207 | } 208 | }) 209 | } 210 | 211 | for d.Len() > 0 { 212 | tmp, tmp2 = d.PopFront() 213 | } 214 | } 215 | 216 | func BenchmarkStableStack(b *testing.B) { 217 | d := deque.New[*testData]() 218 | for i := 0; i < fillCount; i++ { 219 | d.PushBack(nil) 220 | } 221 | 222 | for _, test := range tests { 223 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 224 | for n := 0; n < b.N; n++ { 225 | for i := 0; i < test.count; i++ { 226 | d.PushFront(nil) 227 | tmp, tmp2 = d.PopFront() 228 | } 229 | } 230 | }) 231 | } 232 | 233 | for d.Len() > 0 { 234 | tmp, tmp2 = d.PopFront() 235 | } 236 | } 237 | 238 | func BenchmarkSlowIncreaseQueue(b *testing.B) { 239 | for _, test := range tests { 240 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 241 | for n := 0; n < b.N; n++ { 242 | d := deque.New[*testData]() 243 | for i := 0; i < test.count; i++ { 244 | d.PushBack(nil) 245 | d.PushBack(nil) 246 | tmp, tmp2 = d.PopFront() 247 | } 248 | for d.Len() > 0 { 249 | tmp, tmp2 = d.PopFront() 250 | } 251 | } 252 | }) 253 | } 254 | } 255 | 256 | func BenchmarkSlowIncreaseStack(b *testing.B) { 257 | for _, test := range tests { 258 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 259 | for n := 0; n < b.N; n++ { 260 | d := deque.New[*testData]() 261 | for i := 0; i < test.count; i++ { 262 | d.PushFront(nil) 263 | d.PushFront(nil) 264 | tmp, tmp2 = d.PopFront() 265 | } 266 | for d.Len() > 0 { 267 | tmp, tmp2 = d.PopFront() 268 | } 269 | } 270 | }) 271 | } 272 | } 273 | 274 | func BenchmarkSlowDecreaseQueue(b *testing.B) { 275 | d := deque.New[*testData]() 276 | for _, test := range tests { 277 | items := test.count / 2 278 | for i := 0; i <= items; i++ { 279 | d.PushBack(nil) 280 | } 281 | } 282 | 283 | for _, test := range tests { 284 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 285 | for n := 0; n < b.N; n++ { 286 | for i := 0; i < test.count; i++ { 287 | d.PushBack(nil) 288 | tmp, tmp2 = d.PopFront() 289 | if d.Len() > 0 { 290 | tmp, tmp2 = d.PopFront() 291 | } 292 | } 293 | } 294 | }) 295 | } 296 | 297 | for d.Len() > 0 { 298 | tmp, tmp2 = d.PopFront() 299 | } 300 | } 301 | 302 | func BenchmarkSlowDecreaseStack(b *testing.B) { 303 | d := deque.New[*testData]() 304 | for _, test := range tests { 305 | items := test.count / 2 306 | for i := 0; i <= items; i++ { 307 | d.PushFront(nil) 308 | } 309 | } 310 | 311 | for _, test := range tests { 312 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 313 | for n := 0; n < b.N; n++ { 314 | for i := 0; i < test.count; i++ { 315 | d.PushFront(nil) 316 | tmp, tmp2 = d.PopFront() 317 | if d.Len() > 0 { 318 | tmp, tmp2 = d.PopFront() 319 | } 320 | } 321 | } 322 | }) 323 | } 324 | 325 | for d.Len() > 0 { 326 | tmp, tmp2 = d.PopFront() 327 | } 328 | } 329 | 330 | func BenchmarkMicroserviceQueue(b *testing.B) { 331 | for i, test := range tests { 332 | // Doesn't run the first (0 items) as 0 items makes no sense for this test. 333 | if i == 0 { 334 | continue 335 | } 336 | 337 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 338 | for n := 0; n < b.N; n++ { 339 | d := deque.New[*testData]() 340 | 341 | // Simulate stable traffic 342 | for i := 0; i < test.count; i++ { 343 | d.PushBack(nil) 344 | d.PopFront() 345 | } 346 | 347 | // Simulate slowly increasing traffic 348 | for i := 0; i < test.count; i++ { 349 | d.PushBack(nil) 350 | d.PushBack(nil) 351 | d.PopFront() 352 | } 353 | 354 | // Simulate slowly decreasing traffic, bringing traffic back to normal 355 | for i := 0; i < test.count; i++ { 356 | d.PopFront() 357 | if d.Len() > 0 { 358 | d.PopFront() 359 | } 360 | d.PushBack(nil) 361 | } 362 | 363 | // Simulate quick traffic spike (DDOS attack, etc) 364 | for i := 0; i < test.count; i++ { 365 | d.PushBack(nil) 366 | } 367 | 368 | // Simulate stable traffic while at high traffic 369 | for i := 0; i < test.count; i++ { 370 | d.PushBack(nil) 371 | d.PopFront() 372 | } 373 | 374 | // Simulate going back to normal (DDOS attack fended off) 375 | for i := 0; i < test.count; i++ { 376 | d.PopFront() 377 | } 378 | 379 | // Simulate stable traffic (now that is back to normal) 380 | for i := 0; i < test.count; i++ { 381 | d.PushBack(nil) 382 | d.PopFront() 383 | } 384 | } 385 | }) 386 | } 387 | } 388 | 389 | func BenchmarkMicroserviceStack(b *testing.B) { 390 | for i, test := range tests { 391 | // Doesn't run the first (0 items) as 0 items makes no sense for this test. 392 | if i == 0 { 393 | continue 394 | } 395 | 396 | b.Run(strconv.Itoa(test.count), func(b *testing.B) { 397 | for n := 0; n < b.N; n++ { 398 | d := deque.New[*testData]() 399 | 400 | // Simulate stable traffic 401 | for i := 0; i < test.count; i++ { 402 | d.PushFront(nil) 403 | d.PopFront() 404 | } 405 | 406 | // Simulate slowly increasing traffic 407 | for i := 0; i < test.count; i++ { 408 | d.PushFront(nil) 409 | d.PushFront(nil) 410 | d.PopFront() 411 | } 412 | 413 | // Simulate slowly decreasing traffic, bringing traffic Front to normal 414 | for i := 0; i < test.count; i++ { 415 | d.PopFront() 416 | if d.Len() > 0 { 417 | d.PopFront() 418 | } 419 | d.PushFront(nil) 420 | } 421 | 422 | // Simulate quick traffic spike (DDOS attack, etc) 423 | for i := 0; i < test.count; i++ { 424 | d.PushFront(nil) 425 | } 426 | 427 | // Simulate stable traffic while at high traffic 428 | for i := 0; i < test.count; i++ { 429 | d.PushFront(nil) 430 | d.PopFront() 431 | } 432 | 433 | // Simulate going Front to normal (DDOS attack fended off) 434 | for i := 0; i < test.count; i++ { 435 | d.PopFront() 436 | } 437 | 438 | // Simulate stable traffic (now that is Front to normal) 439 | for i := 0; i < test.count; i++ { 440 | d.PushFront(nil) 441 | d.PopFront() 442 | } 443 | } 444 | }) 445 | } 446 | } 447 | -------------------------------------------------------------------------------- /deque.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ef-ds 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | // Package deque implements a very fast and efficient general purpose queue/stack/deque 22 | // data structure that is specifically optimized to perform when used by 23 | // Microservices and serverless services running in production environments. 24 | package deque 25 | 26 | const ( 27 | // firstSliceSize holds the size of the first slice. 28 | firstSliceSize = 4 29 | 30 | // sliceGrowthFactor determines by how much and how fast the first internal 31 | // slice should grow. A growth factor of 4, firstSliceSize = 4 and maxFirstSliceSize = 64, 32 | // the first slice will start with size 4, then 16 (4*4), then 64 (16*4). 33 | // The growth factor should be tweaked together with firstSliceSize and specially, 34 | // maxFirstSliceSize for maximum efficiency. 35 | // sliceGrowthFactor only applies to the very first slice created. All other 36 | // subsequent slices are created with fixed size of maxInternalSliceSize. 37 | sliceGrowthFactor = 4 38 | 39 | // maxFirstSliceSize holds the maximum size of the first slice. 40 | maxFirstSliceSize = 64 41 | 42 | // maxInternalSliceSize holds the maximum size of each internal slice. 43 | maxInternalSliceSize = 256 44 | 45 | // maxSpareLinks holds the maximum number of spare slices the deque will keep 46 | // when shrinking (items are being removed from the deque). 47 | // 5 means a maximum of 5 slices will be kept as spares, meaning, they 48 | // have been used before to store data, but are now no longer used. 49 | // Spare slices are useful in refill situations, when the deque was filled 50 | // with items and emptied. When the same instance is used to push new items, 51 | // the spare slices from the previous pushes are already allocated and ready 52 | // to be used. So the first pushes will push the data into these slices, 53 | // improving the performance dramatically. 54 | // A higher spare links number means the refills will have a better performance 55 | // for larger number of items (as now there's more spare slices ready to be used). 56 | // The downside is the extra memory usage when the deque shrinks and is 57 | // holding a small amount of items. 58 | maxSpareLinks = 5 59 | ) 60 | 61 | // Deque implements an unbounded, dynamically growing double-ended-queue (deque). 62 | // The zero value for deque is an empty deque ready to use. 63 | type Deque[T any] struct { 64 | // Head points to the first node of the linked list. 65 | head *node[T] 66 | 67 | // Tail points to the last node of the linked list. 68 | // In an empty deque, head and tail points to the same node. 69 | tail *node[T] 70 | 71 | // Hp is the index pointing to the current first element in the deque 72 | // (i.e. first element added in the current deque values). 73 | hp int 74 | 75 | // hlp points to the last index in the head slice. 76 | hlp int 77 | 78 | // tp is the index pointing one beyond the current last element in the deque 79 | // (i.e. last element added in the current deque values). 80 | tp int 81 | 82 | // Len holds the current deque values length. 83 | len int 84 | 85 | // spareLinks holds the number of already used, but now empty, ready-to-be-reused, slices. 86 | spareLinks int 87 | 88 | tZero T 89 | } 90 | 91 | // Node represents a deque node. 92 | // Each node holds a slice of user managed values. 93 | type node[T any] struct { 94 | // v holds the list of user added values in this node. 95 | v []T 96 | 97 | // n points to the next node in the linked list. 98 | n *node[T] 99 | 100 | // p points to the previous node in the linked list. 101 | p *node[T] 102 | } 103 | 104 | // New returns an initialized deque. 105 | func New[T any]() *Deque[T] { 106 | return new(Deque[T]) 107 | } 108 | 109 | // Init initializes or clears deque d. 110 | func (d *Deque[T]) Init() *Deque[T] { 111 | *d = Deque[T]{} 112 | return d 113 | } 114 | 115 | // Len returns the number of elements of deque d. 116 | // The complexity is O(1). 117 | func (d *Deque[T]) Len() int { return d.len } 118 | 119 | // Front returns the first element of deque d or nil if the deque is empty. 120 | // The second, bool result indicates whether a valid value was returned; 121 | // if the deque is empty, false will be returned. 122 | // The complexity is O(1). 123 | func (d *Deque[T]) Front() (T, bool) { 124 | if d.len == 0 { 125 | return d.tZero, false 126 | } 127 | return d.head.v[d.hp], true 128 | } 129 | 130 | // Back returns the last element of deque d or nil if the deque is empty. 131 | // The second, bool result indicates whether a valid value was returned; 132 | // if the deque is empty, false will be returned. 133 | // The complexity is O(1). 134 | func (d *Deque[T]) Back() (T, bool) { 135 | if d.len == 0 { 136 | return d.tZero, false 137 | } 138 | return d.tail.v[d.tp-1], true 139 | } 140 | 141 | // PushFront adds value v to the the front of the deque. 142 | // The complexity is O(1). 143 | func (d *Deque[T]) PushFront(v T) { 144 | switch { 145 | case d.head == nil: 146 | // No nodes present yet. 147 | h := &node[T]{v: make([]T, firstSliceSize)} 148 | h.n = h 149 | h.p = h 150 | d.head = h 151 | d.tail = h 152 | d.tp = firstSliceSize 153 | d.hp = firstSliceSize - 1 154 | d.hlp = d.hp 155 | case d.hp > 0: 156 | // There's already room in the head slice. 157 | d.hp-- 158 | case d.head.p != d.tail: 159 | // There's at least one spare link between head and tail nodes. 160 | d.head = d.head.p 161 | d.hp = len(d.head.v) - 1 162 | d.hlp = d.hp 163 | d.spareLinks-- 164 | if d.len == 0 { 165 | d.tail = d.head 166 | d.tp = len(d.head.v) 167 | } 168 | case len(d.head.v) < maxFirstSliceSize: 169 | // The first slice hasn't grown big enough yet. 170 | l := len(d.head.v) 171 | nl := l * sliceGrowthFactor 172 | n := make([]T, nl) 173 | diff := nl - l 174 | d.tp += diff 175 | d.hp += diff 176 | d.hlp = nl - 1 177 | copy(n[d.hp:], d.head.v) 178 | d.head.v = n 179 | d.hp-- 180 | case d.len == 0: 181 | // The head slice is empty, so reuse it. 182 | d.tail = d.head 183 | d.tp = len(d.head.v) 184 | d.hp = d.tp - 1 185 | d.hlp = d.hp 186 | default: 187 | // No available nodes, so make one. 188 | n := &node[T]{v: make([]T, maxInternalSliceSize)} 189 | n.n = d.head 190 | n.p = d.tail 191 | d.head.p = n 192 | d.tail.n = n 193 | d.head = n 194 | d.hp = maxInternalSliceSize - 1 195 | d.hlp = d.hp 196 | } 197 | d.len++ 198 | d.head.v[d.hp] = v 199 | } 200 | 201 | // PushBack adds value v to the the back of the deque. 202 | // The complexity is O(1). 203 | func (d *Deque[T]) PushBack(v T) { 204 | switch { 205 | case d.head == nil: 206 | // No nodes present yet. 207 | h := &node[T]{v: make([]T, firstSliceSize)} 208 | h.n = h 209 | h.p = h 210 | d.head = h 211 | d.tail = h 212 | d.tail.v[0] = v 213 | d.hlp = firstSliceSize - 1 214 | d.tp = 1 215 | case d.tp < len(d.tail.v): 216 | // There's room in the tail slice. 217 | d.tail.v[d.tp] = v 218 | d.tp++ 219 | case d.tp < maxFirstSliceSize: 220 | // We're on the first slice and it hasn't grown large enough yet. 221 | nv := make([]T, len(d.tail.v)*sliceGrowthFactor) 222 | copy(nv, d.tail.v) 223 | d.tail.v = nv 224 | d.tail.v[d.tp] = v 225 | d.tp++ 226 | d.hlp = len(nv) - 1 227 | case d.tail.n != d.head: 228 | // There's at least one spare link between head and tail nodes. 229 | d.spareLinks-- 230 | n := d.tail.n 231 | d.tail = n 232 | d.tail.v[0] = v 233 | d.tp = 1 234 | default: 235 | // No available nodes, so make one. 236 | n := &node[T]{v: make([]T, maxInternalSliceSize)} 237 | n.n = d.head 238 | n.p = d.tail 239 | d.tail.n = n 240 | d.head.p = n 241 | d.tail = n 242 | d.tail.v[0] = v 243 | d.tp = 1 244 | } 245 | d.len++ 246 | } 247 | 248 | // PopFront retrieves and removes the current element from the front of the deque. 249 | // The second, bool result indicates whether a valid value was returned; 250 | // if the deque is empty, false will be returned. 251 | // The complexity is O(1). 252 | func (d *Deque[T]) PopFront() (T, bool) { 253 | if d.len == 0 { 254 | return d.tZero, false 255 | } 256 | vp := &d.head.v[d.hp] 257 | v := *vp 258 | *vp = d.tZero // Avoid memory leaks 259 | d.len-- 260 | switch { 261 | case d.hp < d.hlp: 262 | // The head isn't at the end of the slice, so just 263 | // move on one place. 264 | d.hp++ 265 | case d.head == d.tail: 266 | // There's only a single element at the end of the slice 267 | // so we can't increment hp, so change tp instead. 268 | d.tp = d.hp 269 | case d.spareLinks >= maxSpareLinks: 270 | // Eliminate this link 271 | d.hp = 0 272 | d.head.p.n = d.head.n 273 | d.head.n.p = d.head.p 274 | d.head = d.head.n 275 | d.hlp = len(d.head.v) - 1 276 | default: 277 | // Leave the link spare. 278 | d.hp = 0 279 | d.head = d.head.n 280 | d.spareLinks++ 281 | d.hlp = len(d.head.v) - 1 282 | } 283 | return v, true 284 | } 285 | 286 | // PopBack retrieves and removes the current element from the back of the deque. 287 | // The second, bool result indicates whether a valid value was returned; 288 | // if the deque is empty, false will be returned. 289 | // The complexity is O(1). 290 | func (d *Deque[T]) PopBack() (T, bool) { 291 | if d.len == 0 { 292 | return d.tZero, false 293 | } 294 | d.len-- 295 | d.tp-- 296 | vp := &d.tail.v[d.tp] 297 | v := *vp 298 | *vp = d.tZero // Avoid memory leaks 299 | switch { 300 | case d.tp > 0: 301 | // There's space before tp. 302 | case d.head == d.tail: 303 | // The list is now empty, so tp==0 is appropriate. 304 | case d.spareLinks >= maxSpareLinks: 305 | // Eliminate this link 306 | d.tail.p.n = d.tail.n 307 | d.tail.n.p = d.tail.p 308 | d.tail = d.tail.p 309 | d.tp = len(d.tail.v) 310 | default: 311 | // Leave the link spare. 312 | d.spareLinks++ 313 | d.tail = d.tail.p 314 | d.tp = len(d.tail.v) 315 | } 316 | return v, true 317 | } 318 | -------------------------------------------------------------------------------- /doc_test.go: -------------------------------------------------------------------------------- 1 | package deque_test 2 | 3 | import ( 4 | "fmt" 5 | 6 | deque "github.com/ef-ds/deque/v2" 7 | ) 8 | 9 | func Example_fIFOQueue() { 10 | var d deque.Deque[int] 11 | for i := 1; i <= 5; i++ { 12 | d.PushBack(i) 13 | } 14 | for d.Len() > 0 { 15 | v, _ := d.PopFront() 16 | fmt.Print(v) 17 | } 18 | // Output: 12345 19 | } 20 | 21 | func Example_stack() { 22 | var d deque.Deque[int] 23 | for i := 1; i <= 5; i++ { 24 | d.PushBack(i) 25 | } 26 | for d.Len() > 0 { 27 | v, _ := d.PopBack() 28 | fmt.Print(v) 29 | } 30 | // Output: 54321 31 | } 32 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ef-ds/deque/v2 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /integration_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ef-ds 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | package deque_test 22 | 23 | import ( 24 | "testing" 25 | 26 | deque "github.com/ef-ds/deque/v2" 27 | ) 28 | 29 | const ( 30 | pushCount = 256 * 3 // Push to fill at least 3 internal slices 31 | ) 32 | 33 | func TestFillQueueShouldRetrieveAllElementsInOrder(t *testing.T) { 34 | var d deque.Deque[int] 35 | 36 | for i := 0; i < pushCount; i++ { 37 | d.PushBack(i) 38 | } 39 | for i := 0; i < pushCount; i++ { 40 | if v, ok := d.PopFront(); !ok || v != i { 41 | t.Errorf("Expected: %d; Got: %d", i, v) 42 | } 43 | } 44 | if d.Len() != 0 { 45 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 46 | } 47 | } 48 | 49 | func TestRefillQueueShouldRetrieveAllElementsInOrder(t *testing.T) { 50 | var d deque.Deque[int] 51 | 52 | for i := 0; i < refillCount; i++ { 53 | for j := 0; j < pushCount; j++ { 54 | d.PushBack(j) 55 | } 56 | for j := 0; j < pushCount; j++ { 57 | if v, ok := d.PopFront(); !ok || v != j { 58 | t.Errorf("Expected: %d; Got: %d", i, v) 59 | } 60 | } 61 | if d.Len() != 0 { 62 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 63 | } 64 | } 65 | } 66 | 67 | func TestRefillFullQueueShouldRetrieveAllElementsInOrder(t *testing.T) { 68 | var d deque.Deque[int] 69 | for i := 0; i < pushCount; i++ { 70 | d.PushBack(i) 71 | } 72 | 73 | for i := 0; i < refillCount; i++ { 74 | for j := 0; j < pushCount; j++ { 75 | d.PushBack(j) 76 | } 77 | for j := 0; j < pushCount; j++ { 78 | if v, ok := d.PopFront(); !ok || v != j { 79 | t.Errorf("Expected: %d; Got: %d", j, v) 80 | } 81 | } 82 | if d.Len() != pushCount { 83 | t.Errorf("Expected: %d; Got: %d", pushCount, d.Len()) 84 | } 85 | } 86 | } 87 | 88 | func TestSlowIncreaseQueueShouldRetrieveAllElementsInOrder(t *testing.T) { 89 | var d deque.Deque[int] 90 | 91 | count := 0 92 | for i := 0; i < pushCount; i++ { 93 | count++ 94 | d.PushBack(count) 95 | count++ 96 | d.PushBack(count) 97 | if v, ok := d.PopFront(); !ok || v != i+1 { 98 | t.Errorf("Expected: %d; Got: %d", i+1, v) 99 | } 100 | } 101 | if d.Len() != pushCount { 102 | t.Errorf("Expected: %d; Got: %d", pushCount, d.Len()) 103 | } 104 | } 105 | 106 | func TestSlowDecreaseQueueShouldRetrieveAllElementsInOrder(t *testing.T) { 107 | var d deque.Deque[int] 108 | push := 0 109 | for i := 0; i < pushCount; i++ { 110 | d.PushBack(push) 111 | push++ 112 | } 113 | 114 | count := -1 115 | for i := 0; i < pushCount-1; i++ { 116 | count++ 117 | if v, ok := d.PopFront(); !ok || v != count { 118 | t.Errorf("Expected: %d; Got: %d", count, v) 119 | } 120 | count++ 121 | if v, ok := d.PopFront(); !ok || v != count { 122 | t.Errorf("Expected: %d; Got: %d", count, v) 123 | } 124 | 125 | d.PushBack(push) 126 | push++ 127 | } 128 | count++ 129 | if v, ok := d.PopFront(); !ok || v != count { 130 | t.Errorf("Expected: %d; Got: %d", count, v) 131 | } 132 | if d.Len() != 0 { 133 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 134 | } 135 | } 136 | 137 | func TestStableQueueShouldRetrieveAllElementsInOrder(t *testing.T) { 138 | var d deque.Deque[int] 139 | 140 | for i := 0; i < pushCount; i++ { 141 | d.PushBack(i) 142 | if v, ok := d.PopFront(); !ok || v != i { 143 | t.Errorf("Expected: %d; Got: %d", i, v) 144 | } 145 | } 146 | if d.Len() != 0 { 147 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 148 | } 149 | } 150 | 151 | func TestStableQueueFullShouldRetrieveAllElementsInOrder(t *testing.T) { 152 | var d deque.Deque[int] 153 | for i := 0; i < pushCount; i++ { 154 | d.PushBack(i) 155 | } 156 | 157 | count := 0 158 | for i := 0; i < pushCount-1; i++ { 159 | d.PushBack(i) 160 | if v, ok := d.PopFront(); !ok || v != count { 161 | t.Errorf("Expected: %d; Got: %d", count, v) 162 | } 163 | count++ 164 | } 165 | if d.Len() != pushCount { 166 | t.Errorf("Expected: %d; Got: %d", pushCount, d.Len()) 167 | } 168 | } 169 | 170 | func TestFillStackShouldRetrieveAllElementsInOrder(t *testing.T) { 171 | var d deque.Deque[int] 172 | 173 | for i := 0; i < pushCount; i++ { 174 | d.PushBack(i) 175 | } 176 | for i := pushCount - 1; i >= 0; i-- { 177 | if v, ok := d.PopBack(); !ok || v != i { 178 | t.Errorf("Expected: %d; Got: %d", i, v) 179 | } 180 | } 181 | if d.Len() != 0 { 182 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 183 | } 184 | } 185 | 186 | func TestRefillStackShouldRetrieveAllElementsInOrder(t *testing.T) { 187 | var d deque.Deque[int] 188 | 189 | for i := 0; i < refillCount; i++ { 190 | for j := 0; j < pushCount; j++ { 191 | d.PushBack(j) 192 | } 193 | for j := pushCount - 1; j >= 0; j-- { 194 | if v, ok := d.PopBack(); !ok || v != j { 195 | t.Errorf("Expected: %d; Got: %d", i, v) 196 | } 197 | } 198 | if d.Len() != 0 { 199 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 200 | } 201 | } 202 | } 203 | 204 | func TestRefillFullStackShouldRetrieveAllElementsInOrder(t *testing.T) { 205 | var d deque.Deque[int] 206 | for i := 0; i < pushCount; i++ { 207 | d.PushBack(i) 208 | } 209 | 210 | for i := 0; i < refillCount; i++ { 211 | for j := 0; j < pushCount; j++ { 212 | d.PushBack(j) 213 | } 214 | for j := pushCount - 1; j >= 0; j-- { 215 | if v, ok := d.PopBack(); !ok || v != j { 216 | t.Errorf("Expected: %d; Got: %d", j, v) 217 | } 218 | } 219 | if d.Len() != pushCount { 220 | t.Errorf("Expected: %d; Got: %d", pushCount, d.Len()) 221 | } 222 | } 223 | } 224 | 225 | func TestSlowIncreaseStackShouldRetrieveAllElementsInOrder(t *testing.T) { 226 | var d deque.Deque[int] 227 | 228 | count := 0 229 | for i := 0; i < pushCount; i++ { 230 | count++ 231 | d.PushBack(count) 232 | count++ 233 | d.PushBack(count) 234 | if v, ok := d.PopBack(); !ok || v != count { 235 | t.Errorf("Expected: %d; Got: %d", count, v) 236 | } 237 | } 238 | if d.Len() != pushCount { 239 | t.Errorf("Expected: %d; Got: %d", pushCount, d.Len()) 240 | } 241 | } 242 | 243 | func TestSlowDecreaseStackShouldRetrieveAllElementsInOrder(t *testing.T) { 244 | var d deque.Deque[int] 245 | push := 0 246 | for i := 0; i < pushCount; i++ { 247 | d.PushBack(push) 248 | push++ 249 | } 250 | 251 | count := push 252 | for i := 0; i < pushCount-1; i++ { 253 | count-- 254 | if v, ok := d.PopBack(); !ok || v != count { 255 | t.Errorf("Expected: %d; Got: %d", count, v) 256 | } 257 | count-- 258 | if v, ok := d.PopBack(); !ok || v != count { 259 | t.Errorf("Expected: %d; Got: %d", count, v) 260 | } 261 | 262 | d.PushBack(count) 263 | count++ 264 | } 265 | count-- 266 | if v, ok := d.PopBack(); !ok || v != count { 267 | t.Errorf("Expected: %d; Got: %d", count, v) 268 | } 269 | if d.Len() != 0 { 270 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 271 | } 272 | } 273 | 274 | func TestStableStackShouldRetrieveAllElementsInOrder(t *testing.T) { 275 | var d deque.Deque[int] 276 | 277 | for i := 0; i < pushCount; i++ { 278 | d.PushBack(i) 279 | if v, ok := d.PopBack(); !ok || v != i { 280 | t.Errorf("Expected: %d; Got: %d", i, v) 281 | } 282 | } 283 | if d.Len() != 0 { 284 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 285 | } 286 | } 287 | 288 | func TestStableFullStackShouldRetrieveAllElementsInOrder(t *testing.T) { 289 | var d deque.Deque[int] 290 | for i := 0; i < pushCount; i++ { 291 | d.PushBack(i) 292 | } 293 | 294 | count := 0 295 | for i := 0; i < pushCount; i++ { 296 | d.PushBack(i) 297 | if v, ok := d.PopFront(); !ok || v != count { 298 | t.Errorf("Expected: %d; Got: %d", count, v) 299 | } 300 | count++ 301 | } 302 | if d.Len() != pushCount { 303 | t.Errorf("Expected: %d; Got: %d", pushCount, d.Len()) 304 | } 305 | } 306 | 307 | func TestPushPopFrontShouldRetrieveAllElementsInOrder(t *testing.T) { 308 | pushPopFrontBackShouldRetrieveAllElementsInOrder( 309 | t, 310 | func(d *deque.Deque[int]) (int, bool) { 311 | return d.PopFront() 312 | }, 313 | func(d *deque.Deque[int]) (int, bool) { 314 | return d.Front() 315 | }, 316 | func(v, lastGet, lastPut int) bool { 317 | return v == lastGet 318 | }, 319 | ) 320 | } 321 | 322 | func TestPushPopBackShouldRetrieveAllElementsInOrder(t *testing.T) { 323 | pushPopFrontBackShouldRetrieveAllElementsInOrder( 324 | t, 325 | func(d *deque.Deque[int]) (int, bool) { 326 | return d.PopBack() 327 | }, 328 | func(d *deque.Deque[int]) (int, bool) { 329 | return d.Back() 330 | }, 331 | func(v, lastGet, lastPut int) bool { 332 | return v == lastPut 333 | }, 334 | ) 335 | } 336 | 337 | func TestMixedPushBackPopFrontBackShouldReturnAllValuesInOrder(t *testing.T) { 338 | var d deque.Deque[int] 339 | for i := 0; i < pushCount; i++ { 340 | d.PushBack(i) 341 | } 342 | count, popFrontCount, popBackCount := 0, 0, pushCount-1 343 | for d.Len() > 0 { 344 | if count%2 == 0 { 345 | if v, ok := d.PopFront(); !ok || v != popFrontCount { 346 | t.Errorf("Expected: %d; Got: %d", popFrontCount, v) 347 | } 348 | popFrontCount++ 349 | } else { 350 | if v, ok := d.PopBack(); !ok || v != popBackCount { 351 | t.Errorf("Expected: %d; Got: %d", popBackCount, v) 352 | } 353 | popBackCount-- 354 | } 355 | if d.Len() != pushCount-count-1 { 356 | t.Errorf("Expected: %d; Got: %d", pushCount-count-1, d.Len()) 357 | } 358 | count++ 359 | } 360 | if _, ok := d.PopBack(); ok { 361 | t.Errorf("Expected: !ok; Got: %t", ok) 362 | } 363 | if d.Len() != 0 { 364 | t.Errorf("Expected: 0; Got: %d", d.Len()) 365 | } 366 | } 367 | 368 | func TestMixedPushFrontPopFrontBackShouldReturnAllValuesInOrder(t *testing.T) { 369 | var d deque.Deque[int] 370 | for i := 0; i < pushCount; i++ { 371 | d.PushFront(i) 372 | } 373 | count, popFrontCount, popBackCount := 0, pushCount-1, 0 374 | for d.Len() > 0 { 375 | if count%2 == 0 { 376 | if v, ok := d.PopFront(); !ok || v != popFrontCount { 377 | t.Errorf("Expected: %d; Got: %d", popFrontCount, v) 378 | } 379 | popFrontCount-- 380 | } else { 381 | if v, ok := d.PopBack(); !ok || v != popBackCount { 382 | t.Errorf("Expected: %d; Got: %d", popBackCount, v) 383 | } 384 | popBackCount++ 385 | } 386 | if d.Len() != pushCount-count-1 { 387 | t.Errorf("Expected: %d; Got: %d", pushCount-count-1, d.Len()) 388 | } 389 | count++ 390 | } 391 | if _, ok := d.PopBack(); ok { 392 | t.Errorf("Expected: !ok; Got: %t", ok) 393 | } 394 | if d.Len() != 0 { 395 | t.Errorf("Expected: 0; Got: %d", d.Len()) 396 | } 397 | } 398 | 399 | func TestMixedPushFrontBackPopFrontShouldReturnAllValuesInOrder(t *testing.T) { 400 | var d deque.Deque[int] 401 | 402 | for i := 0; i < pushCount; i++ { 403 | if i%2 == 0 { 404 | d.PushFront(i) 405 | } else { 406 | d.PushBack(i) 407 | } 408 | } 409 | decValue := true 410 | expectedValue := pushCount - 2 411 | for d.Len() > 0 { 412 | if v, ok := d.PopFront(); !ok || v != expectedValue { 413 | t.Errorf("Expected: %d; Got: %d", expectedValue, v) 414 | } 415 | if decValue { 416 | expectedValue -= 2 417 | } else { 418 | expectedValue += 2 419 | } 420 | if expectedValue == 0 { 421 | decValue = false 422 | } 423 | if !decValue && expectedValue == 2 { 424 | expectedValue = 1 425 | } 426 | } 427 | if _, ok := d.PopFront(); ok { 428 | t.Errorf("Expected: ok; Got: %t", ok) 429 | } 430 | if d.Len() != 0 { 431 | t.Errorf("Expected: 0; Got: %d", d.Len()) 432 | } 433 | } 434 | 435 | func TestMixedPushFrontBackPopBackShouldReturnAllValuesInOrder(t *testing.T) { 436 | var d deque.Deque[int] 437 | 438 | for i := 0; i < pushCount; i++ { 439 | if i%2 == 0 { 440 | d.PushFront(i) 441 | } else { 442 | d.PushBack(i) 443 | } 444 | } 445 | decValue := true 446 | expectedValue := pushCount - 1 447 | for d.Len() > 0 { 448 | if v, ok := d.PopBack(); !ok || v != expectedValue { 449 | t.Errorf("Expected: %d; Got: %d", expectedValue, v) 450 | } 451 | if decValue { 452 | expectedValue -= 2 453 | } else { 454 | expectedValue += 2 455 | } 456 | if expectedValue == -1 { 457 | decValue = false 458 | expectedValue = 0 459 | } 460 | } 461 | if _, ok := d.PopBack(); ok { 462 | t.Errorf("Expected: !ok; Got: %t", ok) 463 | } 464 | if d.Len() != 0 { 465 | t.Errorf("Expected: 0; Got: %d", d.Len()) 466 | } 467 | } 468 | 469 | func TestPushFrontPopFrontRefillWith0ToPushCountItemsShouldReturnAllValuesInOrder(t *testing.T) { 470 | var d deque.Deque[int] 471 | 472 | for i := 0; i < refillCount; i++ { 473 | for k := 0; k < pushCount; k++ { 474 | for j := 0; j < k; j++ { 475 | d.PushFront(j) 476 | } 477 | for j := k; j > 0; j-- { 478 | v, ok := d.PopFront() 479 | if !ok || v != j-1 { 480 | t.Errorf("Expected: %d; Got: %d", j-1, v) 481 | } 482 | } 483 | if d.Len() != 0 { 484 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 485 | } 486 | } 487 | } 488 | } 489 | 490 | func TestPushFrontPopBackRefillWith0ToPushCountItemsShouldReturnAllValuesInOrder(t *testing.T) { 491 | var d deque.Deque[int] 492 | 493 | for i := 0; i < refillCount; i++ { 494 | for k := 0; k < pushCount; k++ { 495 | for j := 0; j < k; j++ { 496 | d.PushFront(j) 497 | } 498 | for j := 0; j < k; j++ { 499 | v, ok := d.PopBack() 500 | if !ok || v != j { 501 | t.Errorf("Expected: %d; Got: %d", j, v) 502 | } 503 | } 504 | if d.Len() != 0 { 505 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 506 | } 507 | } 508 | } 509 | } 510 | 511 | func TestPushBackPopFrontRefillWith0ToPushCountItemsShouldReturnAllValuesInOrder(t *testing.T) { 512 | var d deque.Deque[int] 513 | 514 | for i := 0; i < refillCount; i++ { 515 | for k := 1; k < pushCount; k++ { 516 | for j := 0; j < k; j++ { 517 | d.PushBack(j) 518 | } 519 | for j := 0; j < k; j++ { 520 | v, ok := d.PopFront() 521 | if !ok || v != j { 522 | t.Errorf("Expected: %d; Got: %d", j, v) 523 | } 524 | } 525 | if d.Len() != 0 { 526 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 527 | } 528 | } 529 | } 530 | } 531 | 532 | func TestPushBackPopBackRefillWith0ToPushCountItemsShouldReturnAllValuesInOrder(t *testing.T) { 533 | var d deque.Deque[int] 534 | 535 | for i := 0; i < refillCount; i++ { 536 | for k := 1; k < pushCount; k++ { 537 | for j := 0; j < k; j++ { 538 | d.PushBack(j) 539 | } 540 | for j := k; j > 0; j-- { 541 | v, ok := d.PopBack() 542 | if !ok || v != j-1 { 543 | t.Errorf("Expected: %d; Got: %d", j-1, v) 544 | } 545 | } 546 | if d.Len() != 0 { 547 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 548 | } 549 | } 550 | } 551 | } 552 | 553 | // Helper methods ------------------------------------------------ 554 | 555 | func pushPopFrontBackShouldRetrieveAllElementsInOrder(t *testing.T, popFunc, frontbackFunc func(*deque.Deque[int]) (int, bool), checkValueFunc func(v, lastGet, lastPut int) bool) { 556 | tests := map[string]struct { 557 | putCount []int 558 | getCount []int 559 | }{ 560 | "Test 1 item": { 561 | putCount: []int{1}, 562 | getCount: []int{1}, 563 | }, 564 | "Test 10 item": { 565 | putCount: []int{10}, 566 | getCount: []int{10}, 567 | }, 568 | "Test 100 items": { 569 | putCount: []int{100}, 570 | getCount: []int{100}, 571 | }, 572 | "Test 1000 items": { 573 | putCount: []int{1000}, 574 | getCount: []int{1000}, 575 | }, 576 | "Test 10000 items": { 577 | putCount: []int{10000}, 578 | getCount: []int{10000}, 579 | }, 580 | "Test 100000 items": { 581 | putCount: []int{100000}, 582 | getCount: []int{100000}, 583 | }, 584 | } 585 | 586 | for name, test := range tests { 587 | t.Run(name, func(t *testing.T) { 588 | d := deque.New[int]() 589 | lastPut := 0 590 | lastGet := 0 591 | var ok bool 592 | var v int 593 | for count := 0; count < len(test.getCount); count++ { 594 | for i := 1; i <= test.putCount[count]; i++ { 595 | lastPut++ 596 | d.PushBack(lastPut) 597 | if v, ok = frontbackFunc(d); !ok || !checkValueFunc(v, lastGet+1, lastPut) { 598 | t.Errorf("Expected: %d; Got: %d", lastGet, v) 599 | } 600 | } 601 | 602 | for i := 1; i <= test.getCount[count]; i++ { 603 | lastGet++ 604 | v, ok = frontbackFunc(d) 605 | if !ok || !checkValueFunc(v, lastGet, lastPut-lastGet+1) { 606 | t.Errorf("Expected: %d; Got: %d or %d", lastGet, lastPut-lastGet+1, v) 607 | } 608 | v, ok = popFunc(d) 609 | if !ok || !checkValueFunc(v, lastGet, lastPut-lastGet+1) { 610 | t.Errorf("Expected: %d; Got: %d or %d", lastGet, lastPut-lastGet+1, v) 611 | } 612 | } 613 | } 614 | 615 | if d.Len() != 0 { 616 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 617 | } 618 | if v, ok = frontbackFunc(d); ok { 619 | t.Errorf("Expected: !ok as the queue should be empty; Got: %t", ok) 620 | } 621 | if v, ok = popFunc(d); ok { 622 | t.Errorf("Expected: !ok as the queue should be empty; Got: %t", ok) 623 | } 624 | }) 625 | } 626 | } 627 | -------------------------------------------------------------------------------- /testdata/deque.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef-ds/deque/1a01c6d528a2905bff0ef9e5a4e9332cc4d7c93d/testdata/deque.jpg -------------------------------------------------------------------------------- /testdata/release_v1.0.1.md: -------------------------------------------------------------------------------- 1 | # v1.0.0 vs v1.0.1 2 | 3 | The performance impact is small with a slightly better performance on the queue tests, but slightly worse in the stack tests. 4 | 5 | The overall memory impact is positive with deque now using ~5% less memory for large data sets (>= 10000), but now that the spare slices logic is actually correct, the refill tests for large data sets took a big hit for data sets above 1000 items (deque uses now ~2x as much memory on this scenario). This is expected as with a maxSpareLinks set to 4 and maxInternalSliceSize to 256, the spare slices should be reused only for the first 1000 items or so. Past that, new slices have to be created, which explains the extra memory footprint. 6 | 7 | ``` 8 | benchstat testdata/BenchmarkFillDequeQueue.txt testdata/BenchmarkFillDequeQueue2.txt 9 | name old time/op new time/op delta 10 | /0-4 39.9ns ± 9% 37.4ns ± 1% -6.06% (p=0.000 n=10+9) 11 | /1-4 142ns ± 1% 144ns ± 7% ~ (p=0.896 n=10+9) 12 | /10-4 636ns ± 1% 664ns ± 9% +4.39% (p=0.018 n=9+10) 13 | /100-4 4.74µs ± 3% 4.77µs ± 6% ~ (p=0.922 n=9+10) 14 | /1000-4 43.0µs ±23% 38.2µs ± 2% -11.16% (p=0.000 n=10+9) 15 | /10000-4 450µs ±19% 396µs ± 5% -11.92% (p=0.004 n=10+10) 16 | /100000-4 4.24ms ± 4% 4.09ms ± 4% -3.38% (p=0.011 n=10+10) 17 | /1000000-4 46.8ms ± 1% 48.8ms ±10% +4.21% (p=0.021 n=8+9) 18 | 19 | name old alloc/op new alloc/op delta 20 | /0-4 64.0B ± 0% 64.0B ± 0% ~ (all equal) 21 | /1-4 144B ± 0% 144B ± 0% ~ (all equal) 22 | /10-4 608B ± 0% 608B ± 0% ~ (all equal) 23 | /100-4 6.19kB ± 0% 6.19kB ± 0% ~ (all equal) 24 | /1000-4 33.0kB ± 0% 33.0kB ± 0% ~ (all equal) 25 | /10000-4 322kB ± 0% 322kB ± 0% ~ (all equal) 26 | /100000-4 3.22MB ± 0% 3.22MB ± 0% ~ (all equal) 27 | /1000000-4 32.2MB ± 0% 32.2MB ± 0% ~ (all equal) 28 | 29 | name old allocs/op new allocs/op delta 30 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 31 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 32 | /10-4 15.0 ± 0% 15.0 ± 0% ~ (all equal) 33 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 34 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 35 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 36 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 37 | /1000000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 38 | ``` 39 | 40 | ``` 41 | benchstat testdata/BenchmarkFillDequeStack.txt testdata/BenchmarkFillDequeStack2.txt 42 | name old time/op new time/op delta 43 | /0-4 39.0ns ± 6% 39.7ns ± 5% ~ (p=0.062 n=9+10) 44 | /1-4 145ns ± 2% 148ns ± 4% +2.10% (p=0.034 n=8+9) 45 | /10-4 615ns ± 0% 635ns ± 2% +3.21% (p=0.000 n=8+8) 46 | /100-4 4.42µs ± 1% 4.53µs ± 2% +2.61% (p=0.000 n=10+10) 47 | /1000-4 36.2µs ± 1% 37.9µs ± 4% +4.85% (p=0.000 n=10+10) 48 | /10000-4 383µs ± 7% 392µs ± 6% ~ (p=0.280 n=10+10) 49 | /100000-4 4.10ms ± 9% 4.08ms ± 5% ~ (p=0.739 n=10+10) 50 | /1000000-4 44.5ms ± 4% 47.6ms ± 7% +6.96% (p=0.003 n=9+10) 51 | 52 | name old alloc/op new alloc/op delta 53 | /0-4 64.0B ± 0% 64.0B ± 0% ~ (all equal) 54 | /1-4 144B ± 0% 144B ± 0% ~ (all equal) 55 | /10-4 608B ± 0% 608B ± 0% ~ (all equal) 56 | /100-4 6.19kB ± 0% 6.19kB ± 0% ~ (all equal) 57 | /1000-4 33.0kB ± 0% 33.0kB ± 0% ~ (all equal) 58 | /10000-4 322kB ± 0% 322kB ± 0% ~ (all equal) 59 | /100000-4 3.22MB ± 0% 3.22MB ± 0% ~ (all equal) 60 | /1000000-4 32.2MB ± 0% 32.2MB ± 0% ~ (all equal) 61 | 62 | name old allocs/op new allocs/op delta 63 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 64 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 65 | /10-4 15.0 ± 0% 15.0 ± 0% ~ (all equal) 66 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 67 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 68 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 69 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 70 | /1000000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 71 | ``` 72 | 73 | ``` 74 | benchstat testdata/BenchmarkMicroserviceDequeQueue.txt testdata/BenchmarkMicroserviceDequeQueue2.txt 75 | name old time/op new time/op delta 76 | /1-4 525ns ± 4% 512ns ± 3% -2.38% (p=0.041 n=10+10) 77 | /10-4 3.66µs ± 5% 3.65µs ± 3% ~ (p=0.921 n=10+9) 78 | /100-4 25.9µs ± 2% 25.6µs ± 3% ~ (p=0.089 n=10+10) 79 | /1000-4 241µs ± 3% 233µs ± 3% -3.36% (p=0.003 n=10+9) 80 | /10000-4 2.44ms ± 2% 2.47ms ± 6% ~ (p=0.356 n=10+9) 81 | /100000-4 27.1ms ± 3% 26.9ms ± 3% ~ (p=0.460 n=10+8) 82 | /1000000-4 282ms ± 6% 270ms ± 2% -4.23% (p=0.003 n=9+9) 83 | 84 | name old alloc/op new alloc/op delta 85 | /1-4 560B ± 0% 560B ± 0% ~ (all equal) 86 | /10-4 5.71kB ± 0% 5.71kB ± 0% ~ (all equal) 87 | /100-4 20.9kB ± 0% 20.9kB ± 0% ~ (all equal) 88 | /1000-4 138kB ± 0% 134kB ± 0% -3.00% (p=0.000 n=10+10) 89 | /10000-4 1.54MB ± 0% 1.44MB ± 0% -6.21% (p=0.000 n=10+10) 90 | /100000-4 15.3MB ± 0% 14.4MB ± 0% -5.43% (p=0.000 n=9+10) 91 | /1000000-4 152MB ± 0% 144MB ± 0% -5.32% (p=0.002 n=8+10) 92 | 93 | name old allocs/op new allocs/op delta 94 | /1-4 12.0 ± 0% 12.0 ± 0% ~ (all equal) 95 | /10-4 77.0 ± 0% 77.0 ± 0% ~ (all equal) 96 | /100-4 709 ± 0% 709 ± 0% ~ (all equal) 97 | /1000-4 7.02k ± 0% 7.01k ± 0% -0.03% (p=0.000 n=10+10) 98 | /10000-4 70.2k ± 0% 70.2k ± 0% -0.07% (p=0.000 n=10+10) 99 | /100000-4 702k ± 0% 702k ± 0% -0.06% (p=0.000 n=10+10) 100 | /1000000-4 7.02M ± 0% 7.02M ± 0% -0.06% (p=0.000 n=10+10) 101 | ``` 102 | 103 | ``` 104 | benchstat testdata/BenchmarkMicroserviceDequeStack.txt testdata/BenchmarkMicroserviceDequeStack2.txt 105 | name old time/op new time/op delta 106 | /1-4 419ns ± 8% 413ns ± 7% ~ (p=0.446 n=9+10) 107 | /10-4 2.61µs ± 6% 2.70µs ± 6% +3.45% (p=0.034 n=10+8) 108 | /100-4 25.2µs ± 5% 26.7µs ± 2% +5.83% (p=0.000 n=10+10) 109 | /1000-4 234µs ± 6% 234µs ± 5% ~ (p=0.842 n=10+9) 110 | /10000-4 2.36ms ± 7% 2.34ms ± 4% ~ (p=0.780 n=10+9) 111 | /100000-4 24.2ms ± 2% 26.7ms ± 8% +10.19% (p=0.000 n=9+10) 112 | /1000000-4 249ms ± 2% 257ms ± 2% +3.51% (p=0.000 n=10+10) 113 | 114 | name old alloc/op new alloc/op delta 115 | /1-4 304B ± 0% 304B ± 0% ~ (all equal) 116 | /10-4 1.57kB ± 0% 1.57kB ± 0% ~ (all equal) 117 | /100-4 20.9kB ± 0% 20.9kB ± 0% ~ (all equal) 118 | /1000-4 130kB ± 0% 130kB ± 0% ~ (all equal) 119 | /10000-4 1.29MB ± 0% 1.29MB ± 0% ~ (all equal) 120 | /100000-4 12.8MB ± 0% 12.8MB ± 0% ~ (p=0.173 n=10+9) 121 | /1000000-4 128MB ± 0% 128MB ± 0% ~ (all equal) 122 | 123 | name old allocs/op new allocs/op delta 124 | /1-4 11.0 ± 0% 11.0 ± 0% ~ (all equal) 125 | /10-4 75.0 ± 0% 75.0 ± 0% ~ (all equal) 126 | /100-4 709 ± 0% 709 ± 0% ~ (all equal) 127 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 128 | /10000-4 70.1k ± 0% 70.1k ± 0% ~ (all equal) 129 | /100000-4 701k ± 0% 701k ± 0% ~ (all equal) 130 | /1000000-4 7.01M ± 0% 7.01M ± 0% ~ (all equal) 131 | ``` 132 | 133 | ``` 134 | benchstat testdata/BenchmarkRefillDequeQueue.txt testdata/BenchmarkRefillDequeQueue2.txt 135 | name old time/op new time/op delta 136 | /1-4 3.79µs ± 1% 3.78µs ± 4% ~ (p=0.780 n=9+10) 137 | /10-4 37.8µs ± 7% 39.9µs ±23% ~ (p=0.912 n=10+10) 138 | /100-4 361µs ± 7% 362µs ± 4% ~ (p=0.931 n=9+9) 139 | /1000-4 3.75ms ± 4% 3.72ms ± 4% ~ (p=0.481 n=10+10) 140 | /10000-4 36.5ms ± 3% 41.1ms ± 7% +12.76% (p=0.000 n=10+10) 141 | /100000-4 380ms ± 1% 423ms ± 6% +11.18% (p=0.000 n=10+10) 142 | 143 | name old alloc/op new alloc/op delta 144 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 145 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 146 | /100-4 160kB ± 0% 160kB ± 0% -0.00% (p=0.002 n=8+10) 147 | /1000-4 2.42MB ± 0% 1.60MB ± 0% -33.77% (p=0.000 n=10+9) 148 | /10000-4 17.0MB ± 0% 30.5MB ± 0% +78.96% (p=0.000 n=8+10) 149 | /100000-4 162MB ± 0% 320MB ± 0% +97.98% (p=0.000 n=8+9) 150 | 151 | name old allocs/op new allocs/op delta 152 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 153 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 154 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 155 | /1000-4 100k ± 0% 100k ± 0% -0.39% (p=0.000 n=10+10) 156 | /10000-4 1.00M ± 0% 1.01M ± 0% +0.65% (p=0.000 n=8+10) 157 | /100000-4 10.0M ± 0% 10.1M ± 0% +0.76% (p=0.000 n=10+10) 158 | ``` 159 | 160 | ``` 161 | benchstat testdata/BenchmarkRefillDequeStack.txt testdata/BenchmarkRefillDequeStack2.txt 162 | name old time/op new time/op delta 163 | /1-4 3.62µs ± 1% 3.77µs ± 8% +4.14% (p=0.002 n=10+10) 164 | /10-4 34.1µs ± 5% 36.5µs ± 8% +6.99% (p=0.000 n=10+10) 165 | /100-4 332µs ± 2% 346µs ± 2% +4.47% (p=0.000 n=10+10) 166 | /1000-4 3.28ms ± 2% 3.41ms ± 1% +4.12% (p=0.000 n=10+9) 167 | /10000-4 34.0ms ± 3% 34.4ms ± 2% ~ (p=0.065 n=9+10) 168 | /100000-4 370ms ± 5% 378ms ± 4% +2.29% (p=0.028 n=9+10) 169 | 170 | name old alloc/op new alloc/op delta 171 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 172 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 173 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 174 | /1000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 175 | /10000-4 16.0MB ± 0% 16.0MB ± 0% ~ (p=0.173 n=10+9) 176 | /100000-4 161MB ± 0% 161MB ± 0% -0.00% (p=0.000 n=9+9) 177 | 178 | name old allocs/op new allocs/op delta 179 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 180 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 181 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 182 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 183 | /10000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 184 | /100000-4 10.0M ± 0% 10.0M ± 0% -0.00% (p=0.000 n=10+10) 185 | ``` 186 | 187 | ``` 188 | benchstat testdata/BenchmarkRefillFullDequeQueue.txt testdata/BenchmarkRefillFullDequeQueue2.txt 189 | name old time/op new time/op delta 190 | /1-4 3.75µs ± 4% 3.78µs ± 5% ~ (p=0.565 n=10+9) 191 | /10-4 37.8µs ± 4% 38.9µs ± 7% ~ (p=0.053 n=10+9) 192 | /100-4 371µs ± 3% 377µs ± 9% ~ (p=0.579 n=10+10) 193 | /1000-4 4.02ms ± 5% 3.68ms ± 6% -8.46% (p=0.000 n=10+10) 194 | /10000-4 39.4ms ± 5% 40.6ms ± 3% ~ (p=0.053 n=10+9) 195 | /100000-4 392ms ± 3% 485ms ±11% +23.83% (p=0.000 n=9+8) 196 | 197 | name old alloc/op new alloc/op delta 198 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 199 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 200 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 201 | /1000-4 2.40MB ± 0% 1.60MB ± 0% -33.41% (p=0.000 n=10+10) 202 | /10000-4 16.6MB ± 0% 30.5MB ± 0% +83.39% (p=0.000 n=10+10) 203 | /100000-4 161MB ± 0% 320MB ± 0% +98.91% (p=0.000 n=8+10) 204 | 205 | name old allocs/op new allocs/op delta 206 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 207 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 208 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 209 | /1000-4 100k ± 0% 100k ± 0% -0.39% (p=0.000 n=10+10) 210 | /10000-4 1.00M ± 0% 1.01M ± 0% +0.67% (p=0.000 n=10+10) 211 | /100000-4 10.0M ± 0% 10.1M ± 0% +0.77% (p=0.000 n=10+7) 212 | ``` 213 | 214 | ``` 215 | benchstat testdata/BenchmarkRefillFullDequeStack.txt testdata/BenchmarkRefillFullDequeStack2.txt 216 | name old time/op new time/op delta 217 | /1-4 3.90µs ± 6% 4.53µs ± 4% +16.03% (p=0.000 n=10+9) 218 | /10-4 34.6µs ± 2% 36.4µs ± 5% +5.07% (p=0.000 n=9+10) 219 | /100-4 337µs ± 4% 358µs ±10% +6.21% (p=0.031 n=9+9) 220 | /1000-4 3.58ms ± 8% 3.43ms ± 1% ~ (p=0.156 n=10+9) 221 | /10000-4 34.9ms ± 7% 34.9ms ± 4% ~ (p=0.780 n=10+9) 222 | /100000-4 339ms ± 2% 353ms ± 8% +4.32% (p=0.000 n=9+9) 223 | 224 | name old alloc/op new alloc/op delta 225 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 226 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 227 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 228 | /1000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 229 | /10000-4 16.0MB ± 0% 16.0MB ± 0% -0.00% (p=0.013 n=10+8) 230 | /100000-4 160MB ± 0% 160MB ± 0% ~ (all equal) 231 | 232 | name old allocs/op new allocs/op delta 233 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 234 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 235 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 236 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 237 | /10000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 238 | /100000-4 10.0M ± 0% 10.0M ± 0% ~ (all equal) 239 | ``` 240 | 241 | ``` 242 | benchstat testdata/BenchmarkSlowIncreaseDequeQueue.txt testdata/BenchmarkSlowIncreaseDequeQueue2.txt 243 | name old time/op new time/op delta 244 | /1-4 244ns ± 1% 260ns ± 7% +6.43% (p=0.000 n=10+10) 245 | /10-4 1.86µs ± 1% 1.91µs ± 8% +2.93% (p=0.045 n=10+10) 246 | /100-4 8.02µs ± 1% 7.90µs ± 2% -1.43% (p=0.002 n=10+9) 247 | /1000-4 73.5µs ± 1% 73.5µs ± 8% ~ (p=0.447 n=10+9) 248 | /10000-4 725µs ± 1% 714µs ± 2% -1.55% (p=0.015 n=10+10) 249 | /100000-4 8.20ms ± 0% 8.36ms ± 8% ~ (p=1.000 n=9+10) 250 | /1000000-4 86.4ms ± 1% 87.6ms ±13% ~ (p=0.720 n=10+9) 251 | 252 | name old alloc/op new alloc/op delta 253 | /1-4 224B ± 0% 224B ± 0% ~ (all equal) 254 | /10-4 4.91kB ± 0% 4.91kB ± 0% ~ (all equal) 255 | /100-4 7.79kB ± 0% 7.79kB ± 0% ~ (all equal) 256 | /1000-4 54.1kB ± 0% 54.1kB ± 0% ~ (all equal) 257 | /10000-4 491kB ± 0% 491kB ± 0% ~ (all equal) 258 | /100000-4 4.83MB ± 0% 4.83MB ± 0% ~ (all equal) 259 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% ~ (all equal) 260 | 261 | name old allocs/op new allocs/op delta 262 | /1-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) 263 | /10-4 27.0 ± 0% 27.0 ± 0% ~ (all equal) 264 | /100-4 207 ± 0% 207 ± 0% ~ (all equal) 265 | /1000-4 2.02k ± 0% 2.02k ± 0% ~ (all equal) 266 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 267 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 268 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 269 | ``` 270 | 271 | ``` 272 | benchstat testdata/BenchmarkSlowIncreaseDequeStack.txt testdata/BenchmarkSlowIncreaseDequeStack2.txt 273 | name old time/op new time/op delta 274 | /1-4 240ns ± 0% 245ns ± 2% +1.84% (p=0.000 n=9+8) 275 | /10-4 929ns ± 1% 946ns ± 0% +1.79% (p=0.000 n=10+9) 276 | /100-4 8.65µs ± 1% 8.94µs ± 1% +3.37% (p=0.000 n=10+10) 277 | /1000-4 66.7µs ± 1% 69.6µs ± 5% +4.39% (p=0.000 n=10+9) 278 | /10000-4 666µs ± 1% 695µs ± 1% +4.35% (p=0.000 n=10+9) 279 | /100000-4 7.78ms ± 1% 8.28ms ± 6% +6.45% (p=0.000 n=9+10) 280 | /1000000-4 81.9ms ± 1% 94.5ms ± 8% +15.37% (p=0.000 n=9+10) 281 | 282 | name old alloc/op new alloc/op delta 283 | /1-4 224B ± 0% 224B ± 0% ~ (all equal) 284 | /10-4 768B ± 0% 768B ± 0% ~ (all equal) 285 | /100-4 12.9kB ± 0% 12.9kB ± 0% ~ (all equal) 286 | /1000-4 50.0kB ± 0% 50.0kB ± 0% ~ (all equal) 287 | /10000-4 487kB ± 0% 487kB ± 0% ~ (all equal) 288 | /100000-4 4.82MB ± 0% 4.82MB ± 0% ~ (all equal) 289 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% ~ (all equal) 290 | 291 | name old allocs/op new allocs/op delta 292 | /1-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) 293 | /10-4 25.0 ± 0% 25.0 ± 0% ~ (all equal) 294 | /100-4 209 ± 0% 209 ± 0% ~ (all equal) 295 | /1000-4 2.01k ± 0% 2.01k ± 0% ~ (all equal) 296 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 297 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 298 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 299 | ``` 300 | 301 | ``` 302 | benchstat testdata/BenchmarkSlowDecreaseDequeQueue.txt testdata/BenchmarkSlowDecreaseDequeQueue2.txt 303 | name old time/op new time/op delta 304 | /1-4 40.8ns ± 3% 35.7ns ± 1% -12.65% (p=0.000 n=9+10) 305 | /10-4 413ns ± 1% 362ns ± 3% -12.37% (p=0.000 n=8+9) 306 | /100-4 4.05µs ± 5% 3.73µs ± 8% -7.78% (p=0.000 n=10+10) 307 | /1000-4 42.7µs ± 9% 36.3µs ± 9% -15.08% (p=0.000 n=10+9) 308 | /10000-4 436µs ± 6% 355µs ± 2% -18.58% (p=0.000 n=9+10) 309 | /100000-4 3.92ms ± 2% 3.57ms ± 2% -9.05% (p=0.000 n=9+10) 310 | /1000000-4 39.2ms ± 1% 35.5ms ± 3% -9.33% (p=0.000 n=9+10) 311 | 312 | name old alloc/op new alloc/op delta 313 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 314 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 315 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 316 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 317 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 318 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 319 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 320 | 321 | name old allocs/op new allocs/op delta 322 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 323 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 324 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 325 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 326 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 327 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 328 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 329 | ``` 330 | 331 | ``` 332 | benchstat testdata/BenchmarkSlowIncreaseDequeStack.txt testdata/BenchmarkSlowIncreaseDequeStack2.txt 333 | name old time/op new time/op delta 334 | /1-4 240ns ± 0% 245ns ± 2% +1.84% (p=0.000 n=9+8) 335 | /10-4 929ns ± 1% 946ns ± 0% +1.79% (p=0.000 n=10+9) 336 | /100-4 8.65µs ± 1% 8.94µs ± 1% +3.37% (p=0.000 n=10+10) 337 | /1000-4 66.7µs ± 1% 69.6µs ± 5% +4.39% (p=0.000 n=10+9) 338 | /10000-4 666µs ± 1% 695µs ± 1% +4.35% (p=0.000 n=10+9) 339 | /100000-4 7.78ms ± 1% 8.28ms ± 6% +6.45% (p=0.000 n=9+10) 340 | /1000000-4 81.9ms ± 1% 94.5ms ± 8% +15.37% (p=0.000 n=9+10) 341 | 342 | name old alloc/op new alloc/op delta 343 | /1-4 224B ± 0% 224B ± 0% ~ (all equal) 344 | /10-4 768B ± 0% 768B ± 0% ~ (all equal) 345 | /100-4 12.9kB ± 0% 12.9kB ± 0% ~ (all equal) 346 | /1000-4 50.0kB ± 0% 50.0kB ± 0% ~ (all equal) 347 | /10000-4 487kB ± 0% 487kB ± 0% ~ (all equal) 348 | /100000-4 4.82MB ± 0% 4.82MB ± 0% ~ (all equal) 349 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% ~ (all equal) 350 | 351 | name old allocs/op new allocs/op delta 352 | /1-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) 353 | /10-4 25.0 ± 0% 25.0 ± 0% ~ (all equal) 354 | /100-4 209 ± 0% 209 ± 0% ~ (all equal) 355 | /1000-4 2.01k ± 0% 2.01k ± 0% ~ (all equal) 356 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 357 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 358 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 359 | ``` 360 | 361 | ``` 362 | benchstat testdata/BenchmarkStableDequeQueue.txt testdata/BenchmarkStableDequeQueue2.txt 363 | name old time/op new time/op delta 364 | /1-4 34.8ns ± 1% 35.6ns ± 4% +2.24% (p=0.000 n=10+10) 365 | /10-4 353ns ± 2% 363ns ± 2% +2.84% (p=0.000 n=10+10) 366 | /100-4 3.45µs ± 1% 3.48µs ± 3% ~ (p=0.097 n=10+9) 367 | /1000-4 34.5µs ± 1% 35.5µs ± 4% +2.89% (p=0.001 n=10+10) 368 | /10000-4 346µs ± 2% 349µs ± 3% ~ (p=0.211 n=10+9) 369 | /100000-4 3.43ms ± 1% 3.45ms ± 1% ~ (p=0.075 n=10+10) 370 | /1000000-4 34.4ms ± 1% 34.6ms ± 3% ~ (p=0.853 n=10+10) 371 | 372 | name old alloc/op new alloc/op delta 373 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 374 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 375 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 376 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 377 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 378 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 379 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 380 | 381 | name old allocs/op new allocs/op delta 382 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 383 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 384 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 385 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 386 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 387 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 388 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 389 | ``` 390 | 391 | ``` 392 | benchstat testdata/BenchmarkStableDequeStack.txt testdata/BenchmarkStableDequeStack2.txt 393 | name old time/op new time/op delta 394 | /1-4 35.3ns ± 2% 37.8ns ± 2% +6.99% (p=0.000 n=10+9) 395 | /10-4 355ns ± 0% 383ns ± 3% +8.08% (p=0.000 n=7+9) 396 | /100-4 3.46µs ± 1% 3.72µs ± 2% +7.33% (p=0.000 n=10+9) 397 | /1000-4 34.6µs ± 1% 37.9µs ± 5% +9.42% (p=0.000 n=8+10) 398 | /10000-4 406µs ±15% 380µs ± 8% ~ (p=0.079 n=10+9) 399 | /100000-4 3.90ms ±12% 3.80ms ± 4% ~ (p=0.113 n=10+9) 400 | /1000000-4 38.3ms ±18% 40.2ms ±11% ~ (p=0.143 n=10+10) 401 | 402 | name old alloc/op new alloc/op delta 403 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 404 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 405 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 406 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 407 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 408 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 409 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 410 | 411 | name old allocs/op new allocs/op delta 412 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 413 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 414 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 415 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 416 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 417 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 418 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 419 | ``` 420 | -------------------------------------------------------------------------------- /testdata/release_v1.0.2.md: -------------------------------------------------------------------------------- 1 | # v1.0.1 vs v1.0.2 2 | ## Fill tests 3 | ### FIFO queue 4 | ``` 5 | benchstat testdata/BenchmarkFillDequeQueuev1.0.1.txt testdata/BenchmarkFillDequeQueuev1.0.2.txt 6 | name old time/op new time/op delta 7 | /0-4 38.6ns ± 3% 36.1ns ± 4% -6.30% (p=0.000 n=10+10) 8 | /1-4 149ns ± 6% 143ns ± 5% -4.23% (p=0.011 n=10+10) 9 | /10-4 631ns ± 1% 664ns ± 9% +5.37% (p=0.032 n=8+10) 10 | /100-4 4.68µs ± 5% 4.83µs ± 7% ~ (p=0.089 n=10+10) 11 | /1000-4 38.0µs ± 1% 42.4µs ±15% +11.61% (p=0.000 n=9+10) 12 | /10000-4 385µs ± 3% 417µs ±11% +8.42% (p=0.002 n=10+10) 13 | /100000-4 4.00ms ± 1% 4.45ms ±27% +11.41% (p=0.002 n=10+10) 14 | /1000000-4 45.2ms ± 2% 51.9ms ±12% +14.67% (p=0.000 n=9+10) 15 | 16 | name old alloc/op new alloc/op delta 17 | /0-4 64.0B ± 0% 48.0B ± 0% -25.00% (p=0.000 n=10+10) 18 | /1-4 144B ± 0% 128B ± 0% -11.11% (p=0.000 n=10+10) 19 | /10-4 608B ± 0% 592B ± 0% -2.63% (p=0.000 n=10+10) 20 | /100-4 6.19kB ± 0% 6.18kB ± 0% -0.26% (p=0.000 n=10+10) 21 | /1000-4 33.0kB ± 0% 33.0kB ± 0% -0.05% (p=0.000 n=10+10) 22 | /10000-4 322kB ± 0% 322kB ± 0% -0.00% (p=0.000 n=10+10) 23 | /100000-4 3.22MB ± 0% 3.22MB ± 0% -0.00% (p=0.000 n=10+9) 24 | /1000000-4 32.2MB ± 0% 32.2MB ± 0% -0.00% (p=0.000 n=10+10) 25 | 26 | name old allocs/op new allocs/op delta 27 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 28 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 29 | /10-4 15.0 ± 0% 15.0 ± 0% ~ (all equal) 30 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 31 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 32 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 33 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 34 | /1000000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 35 | ``` 36 | 37 | ### LIFO stack 38 | ``` 39 | benchstat testdata/BenchmarkFillDequeStackv1.0.1.txt testdata/BenchmarkFillDequeStackv1.0.2.txt 40 | name old time/op new time/op delta 41 | /0-4 38.2ns ± 6% 38.3ns ± 3% ~ (p=0.712 n=10+8) 42 | /1-4 146ns ± 8% 148ns ± 9% ~ (p=0.645 n=9+10) 43 | /10-4 636ns ± 3% 662ns ±13% ~ (p=0.288 n=10+10) 44 | /100-4 4.70µs ± 4% 4.99µs ± 6% +6.13% (p=0.000 n=10+9) 45 | /1000-4 38.5µs ± 9% 39.5µs ± 8% ~ (p=0.353 n=10+10) 46 | /10000-4 382µs ± 5% 387µs ± 9% ~ (p=0.739 n=10+10) 47 | /100000-4 3.95ms ± 3% 4.13ms ± 9% +4.35% (p=0.015 n=10+10) 48 | /1000000-4 45.2ms ± 3% 46.8ms ± 4% +3.41% (p=0.010 n=8+8) 49 | 50 | name old alloc/op new alloc/op delta 51 | /0-4 64.0B ± 0% 48.0B ± 0% -25.00% (p=0.000 n=10+10) 52 | /1-4 144B ± 0% 128B ± 0% -11.11% (p=0.000 n=10+10) 53 | /10-4 608B ± 0% 592B ± 0% -2.63% (p=0.000 n=10+10) 54 | /100-4 6.19kB ± 0% 6.18kB ± 0% -0.26% (p=0.000 n=10+10) 55 | /1000-4 33.0kB ± 0% 33.0kB ± 0% -0.05% (p=0.000 n=10+10) 56 | /10000-4 322kB ± 0% 322kB ± 0% -0.00% (p=0.000 n=10+10) 57 | /100000-4 3.22MB ± 0% 3.22MB ± 0% -0.00% (p=0.000 n=10+10) 58 | /1000000-4 32.2MB ± 0% 32.2MB ± 0% -0.00% (p=0.000 n=10+9) 59 | 60 | name old allocs/op new allocs/op delta 61 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 62 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 63 | /10-4 15.0 ± 0% 15.0 ± 0% ~ (all equal) 64 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 65 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 66 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 67 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 68 | /1000000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 69 | ``` 70 | 71 | 72 | ## Microservice tests 73 | ### FIFO queue 74 | ``` 75 | benchstat testdata/BenchmarkMicroserviceDequeQueuev1.0.1.txt testdata/BenchmarkMicroserviceDequeQueuev1.0.2.txt 76 | name old time/op new time/op delta 77 | /1-4 512ns ± 3% 510ns ± 5% ~ (p=0.401 n=10+10) 78 | /10-4 3.51µs ± 0% 3.60µs ± 5% ~ (p=0.062 n=9+10) 79 | /100-4 24.9µs ± 1% 24.8µs ± 7% ~ (p=0.842 n=9+10) 80 | /1000-4 243µs ± 5% 237µs ± 5% ~ (p=0.053 n=10+9) 81 | /10000-4 2.38ms ± 1% 2.38ms ± 5% ~ (p=0.400 n=9+10) 82 | /100000-4 25.7ms ± 3% 25.0ms ± 1% -2.99% (p=0.002 n=10+10) 83 | /1000000-4 268ms ± 2% 268ms ± 4% ~ (p=0.720 n=9+10) 84 | 85 | name old alloc/op new alloc/op delta 86 | /1-4 560B ± 0% 544B ± 0% -2.86% (p=0.000 n=10+10) 87 | /10-4 5.71kB ± 0% 5.70kB ± 0% -0.28% (p=0.000 n=10+10) 88 | /100-4 20.9kB ± 0% 15.8kB ± 0% -24.56% (p=0.000 n=10+10) 89 | /1000-4 134kB ± 0% 133kB ± 0% -0.74% (p=0.000 n=10+10) 90 | /10000-4 1.44MB ± 0% 1.43MB ± 0% -0.64% (p=0.000 n=10+10) 91 | /100000-4 14.4MB ± 0% 14.4MB ± 0% -0.04% (p=0.000 n=10+10) 92 | /1000000-4 144MB ± 0% 144MB ± 0% -0.01% (p=0.000 n=10+10) 93 | 94 | name old allocs/op new allocs/op delta 95 | /1-4 12.0 ± 0% 12.0 ± 0% ~ (all equal) 96 | /10-4 77.0 ± 0% 77.0 ± 0% ~ (all equal) 97 | /100-4 709 ± 0% 707 ± 0% -0.28% (p=0.000 n=10+10) 98 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 99 | /10000-4 70.2k ± 0% 70.2k ± 0% -0.01% (p=0.000 n=10+10) 100 | /100000-4 702k ± 0% 702k ± 0% -0.00% (p=0.000 n=10+10) 101 | /1000000-4 7.02M ± 0% 7.02M ± 0% -0.00% (p=0.000 n=10+10) 102 | ``` 103 | 104 | ### LIFO stack 105 | ``` 106 | benchstat testdata/BenchmarkMicroserviceDequeStackv1.0.1.txt testdata/BenchmarkMicroserviceDequeStackv1.0.2.txt 107 | name old time/op new time/op delta 108 | /1-4 408ns ± 4% 389ns ± 1% -4.68% (p=0.000 n=10+10) 109 | /10-4 2.58µs ± 1% 2.51µs ± 1% -2.60% (p=0.000 n=9+10) 110 | /100-4 24.9µs ± 4% 22.8µs ± 1% -8.27% (p=0.000 n=10+10) 111 | /1000-4 230µs ± 7% 220µs ± 2% -4.49% (p=0.001 n=10+9) 112 | /10000-4 2.47ms ± 7% 2.28ms ± 2% -7.85% (p=0.000 n=10+8) 113 | /100000-4 27.2ms ±11% 24.6ms ± 1% -9.57% (p=0.000 n=10+9) 114 | /1000000-4 282ms ± 6% 258ms ± 1% -8.27% (p=0.000 n=10+9) 115 | 116 | name old alloc/op new alloc/op delta 117 | /1-4 304B ± 0% 288B ± 0% -5.26% (p=0.000 n=10+10) 118 | /10-4 1.57kB ± 0% 1.55kB ± 0% -1.02% (p=0.000 n=10+10) 119 | /100-4 20.9kB ± 0% 15.8kB ± 0% -24.56% (p=0.000 n=10+10) 120 | /1000-4 130kB ± 0% 129kB ± 0% -0.76% (p=0.000 n=10+10) 121 | /10000-4 1.43MB ± 0% 1.43MB ± 0% -0.07% (p=0.000 n=10+10) 122 | /100000-4 14.4MB ± 0% 14.4MB ± 0% -0.01% (p=0.000 n=9+10) 123 | /1000000-4 144MB ± 0% 144MB ± 0% -0.00% (p=0.000 n=9+9) 124 | 125 | name old allocs/op new allocs/op delta 126 | /1-4 11.0 ± 0% 11.0 ± 0% ~ (all equal) 127 | /10-4 75.0 ± 0% 75.0 ± 0% ~ (all equal) 128 | /100-4 709 ± 0% 707 ± 0% -0.28% (p=0.000 n=10+10) 129 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 130 | /10000-4 70.2k ± 0% 70.2k ± 0% ~ (all equal) 131 | /100000-4 702k ± 0% 702k ± 0% ~ (all equal) 132 | /1000000-4 7.02M ± 0% 7.02M ± 0% ~ (all equal) 133 | ``` 134 | 135 | ## Other tests 136 | ### FIFO queue 137 | ``` 138 | benchstat testdata/BenchmarkRefillDequeQueuev1.0.1.txt testdata/BenchmarkRefillDequeQueuev1.0.2.txt 139 | name old time/op new time/op delta 140 | /1-4 3.74µs ± 1% 3.97µs ± 6% +6.10% (p=0.000 n=10+9) 141 | /10-4 35.9µs ± 2% 36.8µs ± 5% +2.45% (p=0.011 n=10+10) 142 | /100-4 352µs ± 3% 347µs ± 5% ~ (p=0.165 n=10+10) 143 | /1000-4 3.42ms ± 1% 3.67ms ±15% +7.04% (p=0.000 n=9+10) 144 | /10000-4 37.9ms ± 2% 39.7ms ± 5% +4.55% (p=0.001 n=9+10) 145 | /100000-4 400ms ± 1% 413ms ± 6% ~ (p=0.095 n=10+9) 146 | 147 | name old alloc/op new alloc/op delta 148 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 149 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 150 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 151 | /1000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.000 n=10+10) 152 | /10000-4 30.5MB ± 0% 30.1MB ± 0% -1.27% (p=0.000 n=8+10) 153 | /100000-4 320MB ± 0% 320MB ± 0% -0.13% (p=0.000 n=10+9) 154 | 155 | name old allocs/op new allocs/op delta 156 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 157 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 158 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 159 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 160 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.02% (p=0.000 n=10+10) 161 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.000 n=10+9) 162 | ``` 163 | ``` 164 | benchstat testdata/BenchmarkRefillFullDequeQueuev1.0.1.txt testdata/BenchmarkRefillFullDequeQueuev1.0.2.txt 165 | name old time/op new time/op delta 166 | /1-4 3.90µs ± 4% 3.70µs ± 8% -5.21% (p=0.035 n=10+10) 167 | /10-4 39.4µs ± 8% 37.4µs ± 7% -5.26% (p=0.007 n=10+10) 168 | /100-4 386µs ± 9% 360µs ± 4% -6.83% (p=0.000 n=10+10) 169 | /1000-4 3.81ms ± 9% 3.50ms ± 3% -8.36% (p=0.000 n=10+9) 170 | /10000-4 41.7ms ±10% 40.0ms ± 5% ~ (p=0.079 n=10+9) 171 | /100000-4 443ms ±16% 426ms ±11% ~ (p=0.243 n=10+9) 172 | 173 | name old alloc/op new alloc/op delta 174 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 175 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 176 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 177 | /1000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 178 | /10000-4 30.5MB ± 0% 30.1MB ± 0% -1.36% (p=0.000 n=10+10) 179 | /100000-4 320MB ± 0% 320MB ± 0% -0.13% (p=0.000 n=10+8) 180 | 181 | name old allocs/op new allocs/op delta 182 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 183 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 184 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 185 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 186 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.02% (p=0.000 n=10+10) 187 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.001 n=8+9) 188 | ``` 189 | ``` 190 | benchstat testdata/BenchmarkSlowIncreaseDequeQueuev1.0.1.txt testdata/BenchmarkSlowIncreaseDequeQueuev1.0.2.txt 191 | name old time/op new time/op delta 192 | /1-4 254ns ± 4% 248ns ± 5% ~ (p=0.195 n=9+9) 193 | /10-4 1.95µs ± 3% 2.09µs ±13% +7.15% (p=0.006 n=9+9) 194 | /100-4 8.08µs ± 2% 8.06µs ± 5% ~ (p=0.256 n=10+10) 195 | /1000-4 73.1µs ± 4% 73.5µs ± 5% ~ (p=0.931 n=9+9) 196 | /10000-4 768µs ± 4% 703µs ± 1% -8.48% (p=0.000 n=9+8) 197 | /100000-4 8.89ms ± 4% 8.45ms ± 8% -4.95% (p=0.017 n=9+10) 198 | /1000000-4 91.1ms ± 2% 90.6ms ± 6% ~ (p=0.905 n=10+9) 199 | 200 | name old alloc/op new alloc/op delta 201 | /1-4 224B ± 0% 208B ± 0% -7.14% (p=0.000 n=10+10) 202 | /10-4 4.91kB ± 0% 4.90kB ± 0% -0.33% (p=0.000 n=10+10) 203 | /100-4 7.79kB ± 0% 7.78kB ± 0% -0.21% (p=0.000 n=10+10) 204 | /1000-4 54.1kB ± 0% 53.2kB ± 0% -1.83% (p=0.000 n=10+10) 205 | /10000-4 491kB ± 0% 486kB ± 0% -1.05% (p=0.000 n=10+10) 206 | /100000-4 4.83MB ± 0% 4.82MB ± 0% -0.02% (p=0.000 n=10+10) 207 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% -0.01% (p=0.000 n=10+10) 208 | 209 | name old allocs/op new allocs/op delta 210 | /1-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) 211 | /10-4 27.0 ± 0% 27.0 ± 0% ~ (all equal) 212 | /100-4 207 ± 0% 207 ± 0% ~ (all equal) 213 | /1000-4 2.02k ± 0% 2.02k ± 0% ~ (all equal) 214 | /10000-4 20.1k ± 0% 20.1k ± 0% -0.01% (p=0.000 n=10+10) 215 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 216 | /1000000-4 2.01M ± 0% 2.01M ± 0% -0.00% (p=0.000 n=10+10) 217 | ``` 218 | ``` 219 | benchstat testdata/BenchmarkSlowDecreaseDequeQueuev1.0.1.txt testdata/BenchmarkSlowDecreaseDequeQueuev1.0.2.txt 220 | name old time/op new time/op delta 221 | /1-4 35.4ns ± 1% 35.3ns ± 3% ~ (p=0.956 n=10+10) 222 | /10-4 364ns ± 3% 353ns ± 2% -3.06% (p=0.001 n=10+9) 223 | /100-4 3.50µs ± 1% 3.61µs ±13% ~ (p=0.720 n=10+9) 224 | /1000-4 34.9µs ± 1% 35.2µs ± 2% ~ (p=0.573 n=10+8) 225 | /10000-4 353µs ± 2% 356µs ± 7% ~ (p=0.905 n=10+9) 226 | /100000-4 3.56ms ± 3% 3.51ms ± 3% -1.39% (p=0.035 n=10+10) 227 | /1000000-4 37.5ms ±16% 34.3ms ± 2% -8.38% (p=0.000 n=9+10) 228 | 229 | name old alloc/op new alloc/op delta 230 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 231 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 232 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 233 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 234 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 235 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 236 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (p=0.294 n=8+10) 237 | 238 | name old allocs/op new allocs/op delta 239 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 240 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 241 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 242 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 243 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 244 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 245 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 246 | ``` 247 | ``` 248 | benchstat testdata/BenchmarkStableDequeQueuev1.0.1.txt testdata/BenchmarkStableDequeQueuev1.0.2.txt 249 | name old time/op new time/op delta 250 | /1-4 36.7ns ± 1% 35.1ns ± 2% -4.47% (p=0.000 n=10+9) 251 | /10-4 370ns ± 1% 358ns ± 4% -3.28% (p=0.002 n=10+9) 252 | /100-4 3.62µs ± 1% 3.44µs ± 2% -4.99% (p=0.000 n=10+9) 253 | /1000-4 36.7µs ± 3% 34.2µs ± 2% -6.80% (p=0.000 n=10+9) 254 | /10000-4 345µs ± 2% 357µs ± 6% +3.74% (p=0.028 n=9+10) 255 | /100000-4 3.42ms ± 2% 3.49ms ± 4% +1.98% (p=0.015 n=10+10) 256 | /1000000-4 34.2ms ± 1% 35.6ms ± 3% +4.21% (p=0.000 n=10+9) 257 | 258 | name old alloc/op new alloc/op delta 259 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 260 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 261 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 262 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 263 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 264 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 265 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 266 | 267 | name old allocs/op new allocs/op delta 268 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 269 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 270 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 271 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 272 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 273 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 274 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 275 | ``` 276 | 277 | ### LIFO stack 278 | ``` 279 | benchstat testdata/BenchmarkRefillDequeStackv1.0.1.txt testdata/BenchmarkRefillDequeStackv1.0.2.txt 280 | name old time/op new time/op delta 281 | /1-4 3.63µs ± 3% 4.10µs ±15% +13.16% (p=0.003 n=9+10) 282 | /10-4 36.3µs ± 6% 35.7µs ± 2% ~ (p=0.218 n=10+10) 283 | /100-4 344µs ± 2% 353µs ±10% ~ (p=0.481 n=10+10) 284 | /1000-4 3.42ms ± 3% 3.43ms ± 5% ~ (p=0.604 n=10+9) 285 | /10000-4 37.4ms ± 8% 38.2ms ±11% ~ (p=0.156 n=10+9) 286 | /100000-4 393ms ± 0% 402ms ± 5% ~ (p=0.089 n=10+10) 287 | 288 | name old alloc/op new alloc/op delta 289 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 290 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 291 | /100-4 160kB ± 0% 160kB ± 0% -0.00% (p=0.000 n=10+9) 292 | /1000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.000 n=10+9) 293 | /10000-4 30.5MB ± 0% 30.1MB ± 0% -1.36% (p=0.000 n=8+8) 294 | /100000-4 320MB ± 0% 320MB ± 0% -0.00% (p=0.000 n=10+9) 295 | 296 | name old allocs/op new allocs/op delta 297 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 298 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 299 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 300 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 301 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.02% (p=0.000 n=10+10) 302 | /100000-4 10.1M ± 0% 10.1M ± 0% ~ (all equal) 303 | ``` 304 | ``` 305 | benchstat testdata/BenchmarkRefillFullDequeStackv1.0.1.txt testdata/BenchmarkRefillFullDequeStackv1.0.2.txt 306 | name old time/op new time/op delta 307 | /1-4 4.09µs ±12% 4.33µs ± 5% +5.99% (p=0.016 n=10+9) 308 | /10-4 35.8µs ± 3% 35.8µs ±11% ~ (p=0.661 n=9+10) 309 | /100-4 342µs ± 0% 364µs ±17% ~ (p=0.146 n=8+10) 310 | /1000-4 3.43ms ± 1% 3.40ms ± 3% ~ (p=0.143 n=10+10) 311 | /10000-4 39.1ms ± 1% 36.7ms ± 0% -5.99% (p=0.000 n=8+9) 312 | /100000-4 394ms ± 1% 407ms ±14% ~ (p=0.481 n=10+10) 313 | 314 | name old alloc/op new alloc/op delta 315 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 316 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 317 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 318 | /1000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 319 | /10000-4 30.9MB ± 0% 30.5MB ± 0% -1.34% (p=0.000 n=10+10) 320 | /100000-4 320MB ± 0% 320MB ± 0% -0.13% (p=0.000 n=10+8) 321 | 322 | name old allocs/op new allocs/op delta 323 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 324 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 325 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 326 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 327 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.02% (p=0.000 n=10+10) 328 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.000 n=10+10) 329 | ``` 330 | ``` 331 | benchstat testdata/BenchmarkSlowIncreaseDequeStackv1.0.1.txt testdata/BenchmarkSlowIncreaseDequeStackv1.0.2.txt 332 | name old time/op new time/op delta 333 | /1-4 258ns ± 1% 252ns ± 5% -2.38% (p=0.049 n=9+10) 334 | /10-4 1.04µs ± 2% 1.02µs ± 5% ~ (p=0.305 n=10+8) 335 | /100-4 9.66µs ± 3% 7.95µs ± 5% -17.72% (p=0.000 n=9+10) 336 | /1000-4 75.2µs ± 2% 68.8µs ± 4% -8.60% (p=0.000 n=9+10) 337 | /10000-4 739µs ± 1% 723µs ± 9% ~ (p=0.218 n=10+10) 338 | /100000-4 8.78ms ± 7% 8.33ms ± 4% -5.08% (p=0.000 n=10+9) 339 | /1000000-4 92.8ms ± 1% 89.7ms ± 6% -3.27% (p=0.027 n=8+9) 340 | 341 | name old alloc/op new alloc/op delta 342 | /1-4 224B ± 0% 208B ± 0% -7.14% (p=0.000 n=10+10) 343 | /10-4 768B ± 0% 752B ± 0% -2.08% (p=0.000 n=10+10) 344 | /100-4 12.9kB ± 0% 7.8kB ± 0% -39.78% (p=0.000 n=10+10) 345 | /1000-4 50.0kB ± 0% 49.0kB ± 0% -1.98% (p=0.000 n=10+10) 346 | /10000-4 487kB ± 0% 486kB ± 0% -0.20% (p=0.000 n=10+10) 347 | /100000-4 4.82MB ± 0% 4.82MB ± 0% -0.02% (p=0.000 n=10+10) 348 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% -0.00% (p=0.000 n=10+10) 349 | 350 | name old allocs/op new allocs/op delta 351 | /1-4 6.00 ± 0% 6.00 ± 0% ~ (all equal) 352 | /10-4 25.0 ± 0% 25.0 ± 0% ~ (all equal) 353 | /100-4 209 ± 0% 207 ± 0% -0.96% (p=0.000 n=10+10) 354 | /1000-4 2.01k ± 0% 2.01k ± 0% ~ (all equal) 355 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 356 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 357 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 358 | ``` 359 | ``` 360 | benchstat testdata/BenchmarkSlowDecreaseDequeStackv1.0.1.txt testdata/BenchmarkSlowDecreaseDequeStackv1.0.2.txt 361 | name old time/op new time/op delta 362 | /1-4 40.4ns ±35% 33.9ns ± 1% -16.07% (p=0.000 n=10+8) 363 | /10-4 374ns ±10% 353ns ± 5% -5.78% (p=0.003 n=10+9) 364 | /100-4 3.50µs ± 1% 3.45µs ± 5% -1.52% (p=0.011 n=8+9) 365 | /1000-4 35.7µs ± 6% 33.9µs ± 2% -4.96% (p=0.000 n=9+9) 366 | /10000-4 360µs ± 8% 342µs ± 3% -5.11% (p=0.006 n=10+8) 367 | /100000-4 3.49ms ± 2% 3.56ms ±10% ~ (p=0.720 n=9+10) 368 | /1000000-4 35.1ms ± 1% 36.3ms ±13% ~ (p=0.447 n=9+10) 369 | 370 | name old alloc/op new alloc/op delta 371 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 372 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 373 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 374 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 375 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 376 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 377 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 378 | 379 | name old allocs/op new allocs/op delta 380 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 381 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 382 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 383 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 384 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 385 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 386 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 387 | ``` 388 | ``` 389 | benchstat testdata/BenchmarkStableDequeStackv1.0.1.txt testdata/BenchmarkStableDequeStackv1.0.2.txt 390 | name old time/op new time/op delta 391 | /1-4 37.1ns ± 1% 39.2ns ± 8% +5.78% (p=0.016 n=8+10) 392 | /10-4 374ns ± 1% 368ns ± 2% -1.74% (p=0.004 n=10+9) 393 | /100-4 3.68µs ± 3% 3.65µs ± 4% ~ (p=0.838 n=10+10) 394 | /1000-4 36.5µs ± 1% 36.6µs ± 3% ~ (p=0.931 n=9+9) 395 | /10000-4 374µs ± 7% 381µs ±12% ~ (p=0.631 n=10+10) 396 | /100000-4 3.66ms ± 1% 3.55ms ± 2% -3.01% (p=0.000 n=10+8) 397 | /1000000-4 36.5ms ± 1% 37.3ms ± 7% ~ (p=0.053 n=10+9) 398 | 399 | name old alloc/op new alloc/op delta 400 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 401 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 402 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 403 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 404 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 405 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 406 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 407 | 408 | name old allocs/op new allocs/op delta 409 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 410 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 411 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 412 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 413 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 414 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 415 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 416 | ``` 417 | -------------------------------------------------------------------------------- /testdata/release_v1.0.3.md: -------------------------------------------------------------------------------- 1 | 2 | # v1.0.2 vs v1.0.3 3 | ## Fill tests 4 | ### FIFO queue 5 | ``` 6 | benchstat testdata/BenchmarkFillDequeQueuev1.0.2.txt testdata/BenchmarkFillDequeQueuev1.0.3.txt 7 | name old time/op new time/op delta 8 | /0-4 36.1ns ± 4% 37.4ns ± 1% +3.68% (p=0.000 n=10+10) 9 | /1-4 143ns ± 5% 171ns ± 1% +19.96% (p=0.000 n=10+10) 10 | /10-4 664ns ± 9% 577ns ± 1% -13.20% (p=0.000 n=10+10) 11 | /100-4 4.83µs ± 7% 4.74µs ± 2% ~ (p=0.356 n=10+9) 12 | /1000-4 42.4µs ±15% 37.1µs ± 3% -12.56% (p=0.000 n=10+10) 13 | /10000-4 417µs ±11% 370µs ± 2% -11.29% (p=0.000 n=10+10) 14 | /100000-4 4.45ms ±27% 3.87ms ± 0% -13.12% (p=0.000 n=10+8) 15 | /1000000-4 51.9ms ±12% 44.1ms ± 1% -14.92% (p=0.000 n=10+10) 16 | 17 | name old alloc/op new alloc/op delta 18 | /0-4 48.0B ± 0% 64.0B ± 0% +33.33% (p=0.000 n=10+10) 19 | /1-4 128B ± 0% 192B ± 0% +50.00% (p=0.000 n=10+10) 20 | /10-4 592B ± 0% 592B ± 0% ~ (all equal) 21 | /100-4 6.18kB ± 0% 7.20kB ± 0% +16.58% (p=0.000 n=10+10) 22 | /1000-4 33.0kB ± 0% 34.0kB ± 0% +3.10% (p=0.000 n=10+10) 23 | /10000-4 322kB ± 0% 323kB ± 0% +0.32% (p=0.000 n=10+10) 24 | /100000-4 3.22MB ± 0% 3.22MB ± 0% +0.03% (p=0.000 n=9+10) 25 | /1000000-4 32.2MB ± 0% 32.2MB ± 0% -0.01% (p=0.000 n=10+10) 26 | 27 | name old allocs/op new allocs/op delta 28 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 29 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 30 | /10-4 15.0 ± 0% 14.0 ± 0% -6.67% (p=0.000 n=10+10) 31 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 32 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 33 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 34 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 35 | /1000000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.000 n=10+10) 36 | ``` 37 | 38 | ### LIFO stack 39 | ``` 40 | benchstat testdata/BenchmarkFillDequeStackv1.0.2.txt testdata/BenchmarkFillDequeStackv1.0.3.txt 41 | name old time/op new time/op delta 42 | /0-4 38.3ns ± 3% 37.5ns ± 1% ~ (p=0.085 n=8+9) 43 | /1-4 148ns ± 9% 171ns ± 0% +14.96% (p=0.000 n=10+10) 44 | /10-4 662ns ±13% 579ns ± 1% -12.51% (p=0.000 n=10+10) 45 | /100-4 4.99µs ± 6% 4.75µs ± 2% -4.96% (p=0.000 n=9+10) 46 | /1000-4 39.5µs ± 8% 36.7µs ± 2% -7.02% (p=0.000 n=10+9) 47 | /10000-4 387µs ± 9% 363µs ± 2% -6.36% (p=0.000 n=10+10) 48 | /100000-4 4.13ms ± 9% 3.81ms ± 0% -7.60% (p=0.000 n=10+8) 49 | /1000000-4 46.8ms ± 4% 43.6ms ± 2% -6.78% (p=0.000 n=8+10) 50 | 51 | name old alloc/op new alloc/op delta 52 | /0-4 48.0B ± 0% 64.0B ± 0% +33.33% (p=0.000 n=10+10) 53 | /1-4 128B ± 0% 192B ± 0% +50.00% (p=0.000 n=10+10) 54 | /10-4 592B ± 0% 592B ± 0% ~ (all equal) 55 | /100-4 6.18kB ± 0% 7.20kB ± 0% +16.58% (p=0.000 n=10+10) 56 | /1000-4 33.0kB ± 0% 34.0kB ± 0% +3.10% (p=0.000 n=10+10) 57 | /10000-4 322kB ± 0% 323kB ± 0% +0.32% (p=0.000 n=10+10) 58 | /100000-4 3.22MB ± 0% 3.22MB ± 0% +0.03% (p=0.000 n=10+10) 59 | /1000000-4 32.2MB ± 0% 32.2MB ± 0% -0.01% (p=0.000 n=9+10) 60 | 61 | name old allocs/op new allocs/op delta 62 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 63 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 64 | /10-4 15.0 ± 0% 14.0 ± 0% -6.67% (p=0.000 n=10+10) 65 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 66 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 67 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 68 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 69 | /1000000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.000 n=10+10) 70 | ``` 71 | 72 | ## Microservice tests 73 | ### FIFO queue 74 | ``` 75 | benchstat testdata/BenchmarkMicroserviceDequeQueuev1.0.2.txt testdata/BenchmarkMicroserviceDequeQueuev1.0.3.txt 76 | name old time/op new time/op delta 77 | /1-4 510ns ± 5% 464ns ± 0% -9.01% (p=0.000 n=10+10) 78 | /10-4 3.60µs ± 5% 2.77µs ± 1% -23.12% (p=0.000 n=10+10) 79 | /100-4 24.8µs ± 7% 24.2µs ± 0% -2.52% (p=0.034 n=10+8) 80 | /1000-4 237µs ± 5% 224µs ± 2% -5.60% (p=0.000 n=9+10) 81 | /10000-4 2.38ms ± 5% 2.28ms ± 2% -4.29% (p=0.000 n=10+10) 82 | /100000-4 25.0ms ± 1% 24.8ms ± 2% ~ (p=0.165 n=10+10) 83 | /1000000-4 268ms ± 4% 259ms ± 3% -3.41% (p=0.000 n=10+10) 84 | 85 | name old alloc/op new alloc/op delta 86 | /1-4 544B ± 0% 544B ± 0% ~ (all equal) 87 | /10-4 5.70kB ± 0% 2.58kB ± 0% -54.78% (p=0.000 n=10+10) 88 | /100-4 15.8kB ± 0% 20.9kB ± 0% +32.76% (p=0.000 n=10+10) 89 | /1000-4 133kB ± 0% 134kB ± 0% +0.77% (p=0.000 n=10+10) 90 | /10000-4 1.43MB ± 0% 1.43MB ± 0% +0.07% (p=0.000 n=10+10) 91 | /100000-4 14.4MB ± 0% 14.4MB ± 0% +0.01% (p=0.000 n=10+10) 92 | /1000000-4 144MB ± 0% 144MB ± 0% +0.00% (p=0.000 n=10+10) 93 | 94 | name old allocs/op new allocs/op delta 95 | /1-4 12.0 ± 0% 11.0 ± 0% -8.33% (p=0.000 n=10+10) 96 | /10-4 77.0 ± 0% 75.0 ± 0% -2.60% (p=0.000 n=10+10) 97 | /100-4 707 ± 0% 709 ± 0% +0.28% (p=0.000 n=10+10) 98 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 99 | /10000-4 70.2k ± 0% 70.2k ± 0% ~ (all equal) 100 | /100000-4 702k ± 0% 702k ± 0% ~ (all equal) 101 | /1000000-4 7.02M ± 0% 7.02M ± 0% ~ (all equal) 102 | ``` 103 | 104 | ### LIFO stack 105 | ``` 106 | benchstat testdata/BenchmarkMicroserviceDequeStackv1.0.2.txt testdata/BenchmarkMicroserviceDequeStackv1.0.3.txt 107 | name old time/op new time/op delta 108 | /1-4 389ns ± 1% 356ns ± 0% -8.44% (p=0.000 n=10+8) 109 | /10-4 2.51µs ± 1% 2.59µs ± 7% ~ (p=0.239 n=10+10) 110 | /100-4 22.8µs ± 1% 23.4µs ± 1% +2.39% (p=0.000 n=10+10) 111 | /1000-4 220µs ± 2% 220µs ± 0% ~ (p=0.114 n=9+8) 112 | /10000-4 2.28ms ± 2% 2.27ms ± 1% ~ (p=0.122 n=8+10) 113 | /100000-4 24.6ms ± 1% 24.9ms ± 2% +1.12% (p=0.011 n=9+9) 114 | /1000000-4 258ms ± 1% 259ms ± 2% ~ (p=0.549 n=9+10) 115 | 116 | name old alloc/op new alloc/op delta 117 | /1-4 288B ± 0% 288B ± 0% ~ (all equal) 118 | /10-4 1.55kB ± 0% 1.55kB ± 0% ~ (all equal) 119 | /100-4 15.8kB ± 0% 16.8kB ± 0% +6.49% (p=0.000 n=10+10) 120 | /1000-4 129kB ± 0% 130kB ± 0% +0.79% (p=0.000 n=10+10) 121 | /10000-4 1.43MB ± 0% 1.42MB ± 0% -0.51% (p=0.000 n=10+10) 122 | /100000-4 14.4MB ± 0% 14.4MB ± 0% +0.01% (p=0.000 n=10+9) 123 | /1000000-4 144MB ± 0% 144MB ± 0% +0.00% (p=0.000 n=9+8) 124 | 125 | name old allocs/op new allocs/op delta 126 | /1-4 11.0 ± 0% 10.0 ± 0% -9.09% (p=0.000 n=10+10) 127 | /10-4 75.0 ± 0% 74.0 ± 0% -1.33% (p=0.000 n=10+10) 128 | /100-4 707 ± 0% 707 ± 0% ~ (all equal) 129 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 130 | /10000-4 70.2k ± 0% 70.2k ± 0% -0.01% (p=0.000 n=10+10) 131 | /100000-4 702k ± 0% 702k ± 0% ~ (all equal) 132 | /1000000-4 7.02M ± 0% 7.02M ± 0% ~ (all equal) 133 | ``` 134 | 135 | ## Other tests 136 | ### FIFO queue 137 | ``` 138 | benchstat testdata/BenchmarkRefillDequeQueuev1.0.2.txt testdata/BenchmarkRefillDequeQueuev1.0.3.txt 139 | name old time/op new time/op delta 140 | /1-4 3.97µs ± 6% 3.95µs ± 7% ~ (p=1.000 n=9+10) 141 | /10-4 36.8µs ± 5% 38.9µs ± 8% +5.70% (p=0.009 n=10+10) 142 | /100-4 347µs ± 5% 370µs ± 8% +6.65% (p=0.029 n=10+10) 143 | /1000-4 3.67ms ±15% 3.85ms ±10% ~ (p=0.089 n=10+10) 144 | /10000-4 39.7ms ± 5% 41.0ms ±12% ~ (p=0.796 n=10+10) 145 | /100000-4 413ms ± 6% 438ms ± 7% +6.08% (p=0.022 n=9+10) 146 | 147 | name old alloc/op new alloc/op delta 148 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 149 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 150 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 151 | /1000-4 1.60MB ± 0% 1.60MB ± 0% +0.00% (p=0.000 n=10+10) 152 | /10000-4 30.1MB ± 0% 30.1MB ± 0% -0.00% (p=0.005 n=10+10) 153 | /100000-4 320MB ± 0% 320MB ± 0% -0.00% (p=0.000 n=9+9) 154 | 155 | name old allocs/op new allocs/op delta 156 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 157 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 158 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 159 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 160 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.033 n=10+10) 161 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.000 n=9+10) 162 | ``` 163 | ``` 164 | benchstat testdata/BenchmarkRefillFullDequeQueuev1.0.2.txt testdata/BenchmarkRefillFullDequeQueuev1.0.3.txt 165 | name old time/op new time/op delta 166 | /1-4 3.70µs ± 8% 3.55µs ± 1% -3.98% (p=0.007 n=10+9) 167 | /10-4 37.4µs ± 7% 35.2µs ± 1% -5.86% (p=0.000 n=10+10) 168 | /100-4 360µs ± 4% 344µs ± 1% -4.46% (p=0.000 n=10+9) 169 | /1000-4 3.50ms ± 3% 3.41ms ± 1% -2.38% (p=0.000 n=9+10) 170 | /10000-4 40.0ms ± 5% 40.6ms ±24% ~ (p=0.258 n=9+9) 171 | /100000-4 426ms ±11% 451ms ±15% ~ (p=0.113 n=9+10) 172 | 173 | name old alloc/op new alloc/op delta 174 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 175 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 176 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 177 | /1000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 178 | /10000-4 30.1MB ± 0% 30.1MB ± 0% ~ (p=0.147 n=10+10) 179 | /100000-4 320MB ± 0% 320MB ± 0% ~ (all equal) 180 | 181 | name old allocs/op new allocs/op delta 182 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 183 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 184 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 185 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 186 | /10000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 187 | /100000-4 10.1M ± 0% 10.1M ± 0% ~ (all equal) 188 | ``` 189 | ``` 190 | benchstat testdata/BenchmarkSlowIncreaseDequeQueuev1.0.2.txt testdata/BenchmarkSlowIncreaseDequeQueuev1.0.3.txt 191 | name old time/op new time/op delta 192 | /1-4 248ns ± 5% 241ns ±14% ~ (p=0.952 n=9+10) 193 | /10-4 2.09µs ±13% 1.32µs ±10% -36.69% (p=0.000 n=9+10) 194 | /100-4 8.06µs ± 5% 9.07µs ±11% +12.61% (p=0.000 n=10+10) 195 | /1000-4 73.5µs ± 5% 78.2µs ±10% ~ (p=0.053 n=9+10) 196 | /10000-4 703µs ± 1% 805µs ± 7% +14.57% (p=0.000 n=8+9) 197 | /100000-4 8.45ms ± 8% 8.74ms ±12% ~ (p=0.529 n=10+10) 198 | /1000000-4 90.6ms ± 6% 92.0ms ± 9% ~ (p=0.549 n=9+10) 199 | 200 | name old alloc/op new alloc/op delta 201 | /1-4 208B ± 0% 208B ± 0% ~ (all equal) 202 | /10-4 4.90kB ± 0% 1.78kB ± 0% -63.73% (p=0.000 n=10+10) 203 | /100-4 7.78kB ± 0% 8.80kB ± 0% +13.17% (p=0.000 n=10+10) 204 | /1000-4 53.2kB ± 0% 54.2kB ± 0% +1.93% (p=0.000 n=10+10) 205 | /10000-4 486kB ± 0% 487kB ± 0% +0.21% (p=0.000 n=10+10) 206 | /100000-4 4.82MB ± 0% 4.82MB ± 0% -0.06% (p=0.000 n=10+10) 207 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% +0.00% (p=0.000 n=10+10) 208 | 209 | name old allocs/op new allocs/op delta 210 | /1-4 6.00 ± 0% 5.00 ± 0% -16.67% (p=0.000 n=10+10) 211 | /10-4 27.0 ± 0% 25.0 ± 0% -7.41% (p=0.000 n=10+10) 212 | /100-4 207 ± 0% 207 ± 0% ~ (all equal) 213 | /1000-4 2.02k ± 0% 2.02k ± 0% ~ (all equal) 214 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 215 | /100000-4 201k ± 0% 201k ± 0% -0.00% (p=0.000 n=10+10) 216 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 217 | ``` 218 | ``` 219 | benchstat testdata/BenchmarkSlowDecreaseDequeQueuev1.0.2.txt testdata/BenchmarkSlowDecreaseDequeQueuev1.0.3.txt 220 | name old time/op new time/op delta 221 | /1-4 35.3ns ± 3% 39.2ns ± 7% +11.02% (p=0.000 n=10+9) 222 | /10-4 353ns ± 2% 391ns ±11% +10.86% (p=0.001 n=9+10) 223 | /100-4 3.61µs ±13% 3.71µs ± 8% ~ (p=0.243 n=9+10) 224 | /1000-4 35.2µs ± 2% 36.1µs ± 7% ~ (p=0.274 n=8+10) 225 | /10000-4 356µs ± 7% 375µs ±10% +5.38% (p=0.035 n=9+10) 226 | /100000-4 3.51ms ± 3% 3.68ms ± 8% ~ (p=0.123 n=10+10) 227 | /1000000-4 34.3ms ± 2% 37.0ms ± 9% +7.86% (p=0.015 n=10+10) 228 | 229 | name old alloc/op new alloc/op delta 230 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 231 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 232 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 233 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 234 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 235 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 236 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (p=0.137 n=10+8) 237 | 238 | name old allocs/op new allocs/op delta 239 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 240 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 241 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 242 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 243 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 244 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 245 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 246 | ``` 247 | ``` 248 | benchstat testdata/BenchmarkStableDequeQueuev1.0.2.txt testdata/BenchmarkStableDequeQueuev1.0.3.txt 249 | name old time/op new time/op delta 250 | /1-4 35.1ns ± 2% 37.2ns ± 9% ~ (p=0.128 n=9+10) 251 | /10-4 358ns ± 4% 369ns ± 8% ~ (p=0.138 n=9+10) 252 | /100-4 3.44µs ± 2% 3.52µs ± 8% ~ (p=0.604 n=9+10) 253 | /1000-4 34.2µs ± 2% 36.9µs ± 7% +7.82% (p=0.001 n=9+9) 254 | /10000-4 357µs ± 6% 353µs ± 9% ~ (p=0.393 n=10+10) 255 | /100000-4 3.49ms ± 4% 3.60ms ± 8% ~ (p=0.190 n=10+10) 256 | /1000000-4 35.6ms ± 3% 35.5ms ± 7% ~ (p=0.720 n=9+10) 257 | 258 | name old alloc/op new alloc/op delta 259 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 260 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 261 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 262 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 263 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 264 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 265 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 266 | 267 | name old allocs/op new allocs/op delta 268 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 269 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 270 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 271 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 272 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 273 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 274 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 275 | ``` 276 | 277 | ### LIFO stack 278 | ``` 279 | benchstat testdata/BenchmarkRefillDequeStackv1.0.2.txt testdata/BenchmarkRefillDequeStackv1.0.3.txt 280 | name old time/op new time/op delta 281 | /1-4 4.10µs ±15% 3.91µs ± 8% ~ (p=0.481 n=10+10) 282 | /10-4 35.7µs ± 2% 36.6µs ± 8% ~ (p=0.631 n=10+10) 283 | /100-4 353µs ±10% 372µs ± 9% ~ (p=0.089 n=10+10) 284 | /1000-4 3.43ms ± 5% 3.61ms ± 7% +5.29% (p=0.010 n=9+10) 285 | /10000-4 38.2ms ±11% 42.0ms ± 7% +10.10% (p=0.000 n=9+10) 286 | /100000-4 402ms ± 5% 444ms ±14% +10.37% (p=0.002 n=10+10) 287 | 288 | name old alloc/op new alloc/op delta 289 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 290 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 291 | /100-4 160kB ± 0% 160kB ± 0% +0.00% (p=0.000 n=9+10) 292 | /1000-4 1.60MB ± 0% 1.60MB ± 0% +0.00% (p=0.000 n=9+10) 293 | /10000-4 30.1MB ± 0% 30.1MB ± 0% +0.00% (p=0.000 n=8+8) 294 | /100000-4 320MB ± 0% 320MB ± 0% +0.00% (p=0.000 n=9+9) 295 | 296 | name old allocs/op new allocs/op delta 297 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 298 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 299 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 300 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 301 | /10000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 302 | /100000-4 10.1M ± 0% 10.1M ± 0% ~ (all equal) 303 | ``` 304 | ``` 305 | benchstat testdata/BenchmarkRefillFullDequeStackv1.0.2.txt testdata/BenchmarkRefillFullDequeStackv1.0.3.txt 306 | name old time/op new time/op delta 307 | /1-4 4.33µs ± 5% 3.85µs ± 9% -11.15% (p=0.000 n=9+10) 308 | /10-4 35.8µs ±11% 38.2µs ± 9% +6.82% (p=0.009 n=10+10) 309 | /100-4 364µs ±17% 378µs ± 8% ~ (p=0.063 n=10+10) 310 | /1000-4 3.40ms ± 3% 3.76ms ± 8% +10.57% (p=0.000 n=10+10) 311 | /10000-4 36.7ms ± 0% 42.7ms ±10% +16.29% (p=0.000 n=9+10) 312 | /100000-4 407ms ±14% 446ms ±11% +9.76% (p=0.009 n=10+10) 313 | 314 | name old alloc/op new alloc/op delta 315 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 316 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 317 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 318 | /1000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 319 | /10000-4 30.5MB ± 0% 30.1MB ± 0% -1.36% (p=0.000 n=10+10) 320 | /100000-4 320MB ± 0% 320MB ± 0% ~ (all equal) 321 | 322 | name old allocs/op new allocs/op delta 323 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 324 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 325 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 326 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 327 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.02% (p=0.000 n=10+10) 328 | /100000-4 10.1M ± 0% 10.1M ± 0% ~ (all equal) 329 | ``` 330 | ``` 331 | benchstat testdata/BenchmarkSlowIncreaseDequeStackv1.0.2.txt testdata/BenchmarkSlowIncreaseDequeStackv1.0.3.txt 332 | name old time/op new time/op delta 333 | /1-4 252ns ± 5% 226ns ±10% -10.58% (p=0.001 n=10+10) 334 | /10-4 1.02µs ± 5% 1.06µs ± 7% ~ (p=0.165 n=8+10) 335 | /100-4 7.95µs ± 5% 8.71µs ±13% +9.65% (p=0.003 n=10+10) 336 | /1000-4 68.8µs ± 4% 75.5µs ± 9% +9.86% (p=0.000 n=10+10) 337 | /10000-4 723µs ± 9% 731µs ± 6% ~ (p=0.739 n=10+10) 338 | /100000-4 8.33ms ± 4% 8.81ms ±10% ~ (p=0.182 n=9+10) 339 | /1000000-4 89.7ms ± 6% 94.4ms ±11% ~ (p=0.211 n=9+10) 340 | 341 | name old alloc/op new alloc/op delta 342 | /1-4 208B ± 0% 208B ± 0% ~ (all equal) 343 | /10-4 752B ± 0% 752B ± 0% ~ (all equal) 344 | /100-4 7.78kB ± 0% 8.80kB ± 0% +13.17% (p=0.000 n=10+10) 345 | /1000-4 49.0kB ± 0% 50.0kB ± 0% +2.09% (p=0.000 n=10+10) 346 | /10000-4 486kB ± 0% 483kB ± 0% -0.64% (p=0.000 n=10+10) 347 | /100000-4 4.82MB ± 0% 4.82MB ± 0% +0.02% (p=0.000 n=10+10) 348 | /1000000-4 48.2MB ± 0% 48.2MB ± 0% +0.00% (p=0.000 n=10+10) 349 | 350 | name old allocs/op new allocs/op delta 351 | /1-4 6.00 ± 0% 5.00 ± 0% -16.67% (p=0.000 n=10+10) 352 | /10-4 25.0 ± 0% 24.0 ± 0% -4.00% (p=0.000 n=10+10) 353 | /100-4 207 ± 0% 207 ± 0% ~ (all equal) 354 | /1000-4 2.01k ± 0% 2.01k ± 0% ~ (all equal) 355 | /10000-4 20.1k ± 0% 20.1k ± 0% -0.01% (p=0.000 n=10+10) 356 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 357 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 358 | ``` 359 | ``` 360 | benchstat testdata/BenchmarkSlowDecreaseDequeStackv1.0.2.txt testdata/BenchmarkSlowDecreaseDequeStackv1.0.3.txt 361 | name old time/op new time/op delta 362 | /1-4 33.9ns ± 1% 37.3ns ± 7% +9.94% (p=0.000 n=8+10) 363 | /10-4 353ns ± 5% 386ns ± 9% +9.28% (p=0.001 n=9+10) 364 | /100-4 3.45µs ± 5% 3.76µs ± 8% +9.10% (p=0.001 n=9+10) 365 | /1000-4 33.9µs ± 2% 37.0µs ± 6% +9.17% (p=0.000 n=9+10) 366 | /10000-4 342µs ± 3% 377µs ± 8% +10.20% (p=0.000 n=8+10) 367 | /100000-4 3.56ms ±10% 3.72ms ± 9% +4.49% (p=0.035 n=10+10) 368 | /1000000-4 36.3ms ±13% 38.1ms ± 9% ~ (p=0.105 n=10+10) 369 | 370 | name old alloc/op new alloc/op delta 371 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 372 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 373 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 374 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 375 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 376 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 377 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 378 | 379 | name old allocs/op new allocs/op delta 380 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 381 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 382 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 383 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 384 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 385 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 386 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 387 | ``` 388 | ``` 389 | benchstat testdata/BenchmarkStableDequeStackv1.0.2.txt testdata/BenchmarkStableDequeStackv1.0.3.txt 390 | name old time/op new time/op delta 391 | /1-4 39.2ns ± 8% 35.6ns ± 7% -9.26% (p=0.001 n=10+10) 392 | /10-4 368ns ± 2% 362ns ± 8% ~ (p=0.985 n=9+10) 393 | /100-4 3.65µs ± 4% 3.54µs ± 7% ~ (p=0.123 n=10+10) 394 | /1000-4 36.6µs ± 3% 35.9µs ±10% ~ (p=0.780 n=9+10) 395 | /10000-4 381µs ±12% 360µs ±10% ~ (p=0.063 n=10+10) 396 | /100000-4 3.55ms ± 2% 3.53ms ± 6% ~ (p=0.633 n=8+10) 397 | /1000000-4 37.3ms ± 7% 34.4ms ± 7% -7.60% (p=0.000 n=9+10) 398 | 399 | name old alloc/op new alloc/op delta 400 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 401 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 402 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 403 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 404 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 405 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (all equal) 406 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (all equal) 407 | 408 | name old allocs/op new allocs/op delta 409 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 410 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 411 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 412 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 413 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 414 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 415 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 416 | ``` 417 | -------------------------------------------------------------------------------- /testdata/release_v2.0.0.md: -------------------------------------------------------------------------------- 1 | 2 | # v1.0.3 vs v2.0.0 3 | ## Fill tests 4 | ### FIFO queue 5 | ``` 6 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkFillDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkFillDequeQueue.txt 7 | name old time/op new time/op delta 8 | /0-4 41.1ns ± 1% 40.5ns ± 0% -1.42% (p=0.000 n=8+8) 9 | /1-4 168ns ± 2% 160ns ± 1% -4.62% (p=0.000 n=10+10) 10 | /10-4 579ns ± 1% 621ns ±24% ~ (p=0.730 n=9+9) 11 | /100-4 4.65µs ± 1% 4.15µs ± 6% -10.67% (p=0.000 n=9+9) 12 | /1000-4 36.6µs ± 0% 34.3µs ± 2% -6.39% (p=0.000 n=9+10) 13 | /10000-4 368µs ± 1% 346µs ± 1% -6.02% (p=0.000 n=10+9) 14 | /100000-4 3.78ms ± 0% 3.84ms ± 0% +1.54% (p=0.000 n=8+9) 15 | /1000000-4 44.3ms ± 1% 42.1ms ± 2% -4.92% (p=0.000 n=10+9) 16 | 17 | name old alloc/op new alloc/op delta 18 | /0-4 64.0B ± 0% 64.0B ± 0% ~ (all equal) 19 | /1-4 192B ± 0% 160B ± 0% -16.67% (p=0.000 n=10+10) 20 | /10-4 592B ± 0% 432B ± 0% -27.03% (p=0.000 n=10+10) 21 | /100-4 7.20kB ± 0% 4.48kB ± 0% -37.78% (p=0.000 n=10+10) 22 | /1000-4 34.0kB ± 0% 25.2kB ± 0% -26.05% (p=0.000 n=10+10) 23 | /10000-4 323kB ± 0% 243kB ± 0% -24.93% (p=0.002 n=8+10) 24 | /100000-4 3.22MB ± 0% 2.42MB ± 0% -24.88% (p=0.000 n=9+10) 25 | /1000000-4 32.2MB ± 0% 24.2MB ± 0% -24.85% (p=0.000 n=10+10) 26 | 27 | name old allocs/op new allocs/op delta 28 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 29 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 30 | /10-4 14.0 ± 0% 14.0 ± 0% ~ (all equal) 31 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 32 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 33 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 34 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 35 | /1000000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 36 | ``` 37 | ### LIFO stack 38 | ``` 39 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkFillDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkFillDequeStack.txt 40 | name old time/op new time/op delta 41 | /0-4 40.4ns ± 0% 40.0ns ± 1% -1.09% (p=0.000 n=10+9) 42 | /1-4 163ns ± 0% 158ns ± 2% -3.27% (p=0.000 n=9+9) 43 | /10-4 578ns ± 0% 555ns ± 2% -4.04% (p=0.000 n=10+9) 44 | /100-4 4.59µs ± 0% 4.13µs ± 2% -10.10% (p=0.000 n=8+9) 45 | /1000-4 36.1µs ± 0% 35.0µs ± 7% -3.25% (p=0.003 n=8+10) 46 | /10000-4 365µs ± 1% 349µs ± 2% -4.38% (p=0.000 n=10+10) 47 | /100000-4 3.77ms ± 0% 3.86ms ± 1% +2.44% (p=0.000 n=10+9) 48 | /1000000-4 43.9ms ± 1% 42.3ms ± 3% -3.70% (p=0.000 n=10+10) 49 | 50 | name old alloc/op new alloc/op delta 51 | /0-4 64.0B ± 0% 64.0B ± 0% ~ (all equal) 52 | /1-4 192B ± 0% 160B ± 0% -16.67% (p=0.000 n=10+10) 53 | /10-4 592B ± 0% 432B ± 0% -27.03% (p=0.000 n=10+10) 54 | /100-4 7.20kB ± 0% 4.48kB ± 0% -37.78% (p=0.000 n=10+10) 55 | /1000-4 34.0kB ± 0% 25.2kB ± 0% -26.05% (p=0.000 n=10+10) 56 | /10000-4 323kB ± 0% 243kB ± 0% -24.93% (p=0.002 n=8+10) 57 | /100000-4 3.22MB ± 0% 2.42MB ± 0% -24.88% (p=0.000 n=10+10) 58 | /1000000-4 32.2MB ± 0% 24.2MB ± 0% -24.85% (p=0.000 n=10+9) 59 | 60 | name old allocs/op new allocs/op delta 61 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 62 | /1-4 4.00 ± 0% 4.00 ± 0% ~ (all equal) 63 | /10-4 14.0 ± 0% 14.0 ± 0% ~ (all equal) 64 | /100-4 107 ± 0% 107 ± 0% ~ (all equal) 65 | /1000-4 1.01k ± 0% 1.01k ± 0% ~ (all equal) 66 | /10000-4 10.1k ± 0% 10.1k ± 0% ~ (all equal) 67 | /100000-4 101k ± 0% 101k ± 0% ~ (all equal) 68 | /1000000-4 1.01M ± 0% 1.01M ± 0% ~ (all equal) 69 | ``` 70 | 71 | ## Microservice tests 72 | ### FIFO queue 73 | ``` 74 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkMicroserviceDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkMicroserviceDequeQueue.txt 75 | name old time/op new time/op delta 76 | /0-4 44.3ns ± 0% 45.9ns ± 2% +3.52% (p=0.000 n=9+9) 77 | /1-4 456ns ± 0% 429ns ± 1% -5.78% (p=0.000 n=9+9) 78 | /10-4 2.80µs ± 0% 2.70µs ± 1% -3.61% (p=0.000 n=9+9) 79 | /100-4 24.2µs ± 0% 23.8µs ± 1% -1.82% (p=0.000 n=9+10) 80 | /1000-4 226µs ± 1% 229µs ± 5% ~ (p=0.247 n=10+10) 81 | /10000-4 2.37ms ± 4% 2.32ms ± 3% ~ (p=0.052 n=10+10) 82 | /100000-4 26.2ms ± 1% 25.7ms ± 1% -1.91% (p=0.000 n=10+10) 83 | /1000000-4 266ms ± 1% 274ms ± 1% +2.94% (p=0.000 n=10+10) 84 | 85 | name old alloc/op new alloc/op delta 86 | /0-4 64.0B ± 0% 64.0B ± 0% ~ (all equal) 87 | /1-4 544B ± 0% 384B ± 0% -29.41% (p=0.000 n=10+10) 88 | /10-4 2.58kB ± 0% 1.90kB ± 0% -26.09% (p=0.000 n=10+10) 89 | /100-4 20.9kB ± 0% 16.2kB ± 0% -22.77% (p=0.000 n=10+10) 90 | /1000-4 134kB ± 0% 123kB ± 0% -8.13% (p=0.000 n=10+10) 91 | /10000-4 1.43MB ± 0% 1.28MB ± 0% -10.77% (p=0.000 n=10+10) 92 | /100000-4 14.4MB ± 0% 12.8MB ± 0% -11.05% (p=0.000 n=9+10) 93 | /1000000-4 144MB ± 0% 128MB ± 0% -11.08% (p=0.000 n=10+10) 94 | 95 | name old allocs/op new allocs/op delta 96 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 97 | /1-4 11.0 ± 0% 11.0 ± 0% ~ (all equal) 98 | /10-4 75.0 ± 0% 75.0 ± 0% ~ (all equal) 99 | /100-4 709 ± 0% 709 ± 0% ~ (all equal) 100 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 101 | /10000-4 70.2k ± 0% 70.2k ± 0% ~ (all equal) 102 | /100000-4 702k ± 0% 702k ± 0% ~ (all equal) 103 | /1000000-4 7.02M ± 0% 7.02M ± 0% ~ (p=0.332 n=10+10) 104 | ``` 105 | ### LIFO stack 106 | ``` 107 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkMicroserviceDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkMicroserviceDequeStack.txt 108 | name old time/op new time/op delta 109 | /0-4 45.3ns ± 6% 45.3ns ± 1% ~ (p=0.156 n=10+9) 110 | /1-4 384ns ±12% 356ns ± 0% -7.37% (p=0.000 n=10+9) 111 | /10-4 2.60µs ± 2% 2.50µs ± 1% -3.89% (p=0.000 n=9+10) 112 | /100-4 29.3µs ±80% 22.8µs ± 1% -21.95% (p=0.000 n=9+10) 113 | /1000-4 228µs ± 8% 221µs ± 1% -3.19% (p=0.000 n=8+8) 114 | /10000-4 2.70ms ±14% 2.28ms ± 2% -15.66% (p=0.000 n=10+9) 115 | /100000-4 27.2ms ±17% 24.8ms ± 0% -8.94% (p=0.000 n=9+8) 116 | /1000000-4 265ms ± 1% 267ms ± 1% ~ (p=0.075 n=10+10) 117 | 118 | name old alloc/op new alloc/op delta 119 | /0-4 64.0B ± 0% 64.0B ± 0% ~ (all equal) 120 | /1-4 288B ± 0% 256B ± 0% -11.11% (p=0.000 n=10+10) 121 | /10-4 1.55kB ± 0% 1.39kB ± 0% -10.31% (p=0.000 n=10+10) 122 | /100-4 16.8kB ± 0% 14.1kB ± 0% -16.19% (p=0.000 n=10+10) 123 | /1000-4 130kB ± 0% 121kB ± 0% -6.82% (p=0.000 n=10+10) 124 | /10000-4 1.42MB ± 0% 1.27MB ± 0% -10.55% (p=0.000 n=10+10) 125 | /100000-4 14.4MB ± 0% 12.8MB ± 0% -11.04% (p=0.000 n=10+9) 126 | /1000000-4 144MB ± 0% 128MB ± 0% -11.08% (p=0.000 n=10+8) 127 | 128 | name old allocs/op new allocs/op delta 129 | /0-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 130 | /1-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 131 | /10-4 74.0 ± 0% 74.0 ± 0% ~ (all equal) 132 | /100-4 707 ± 0% 707 ± 0% ~ (all equal) 133 | /1000-4 7.01k ± 0% 7.01k ± 0% ~ (all equal) 134 | /10000-4 70.2k ± 0% 70.2k ± 0% ~ (all equal) 135 | /100000-4 702k ± 0% 702k ± 0% ~ (all equal) 136 | /1000000-4 7.02M ± 0% 7.02M ± 0% ~ (p=0.871 n=10+10) 137 | ``` 138 | 139 | ## Other tests 140 | ### FIFO queue 141 | ``` 142 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillDequeQueue.txt 143 | name old time/op new time/op delta 144 | /1-4 3.61µs ± 0% 3.86µs ± 7% +7.04% (p=0.000 n=10+9) 145 | /10-4 34.9µs ± 1% 34.7µs ± 3% ~ (p=0.258 n=9+9) 146 | /100-4 346µs ± 3% 334µs ± 2% -3.37% (p=0.002 n=10+10) 147 | /1000-4 3.38ms ± 2% 3.28ms ± 2% -2.84% (p=0.001 n=10+10) 148 | /10000-4 37.6ms ± 7% 35.5ms ± 5% -5.60% (p=0.000 n=10+10) 149 | /100000-4 384ms ± 2% 417ms ±10% +8.61% (p=0.000 n=8+10) 150 | 151 | name old alloc/op new alloc/op delta 152 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 153 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 154 | /100-4 160kB ± 0% 160kB ± 0% -0.00% (p=0.000 n=10+8) 155 | /1000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.000 n=7+10) 156 | /10000-4 30.1MB ± 0% 23.1MB ± 0% -23.17% (p=0.000 n=10+9) 157 | /100000-4 320MB ± 0% 241MB ± 0% -24.70% (p=0.000 n=9+10) 158 | 159 | name old allocs/op new allocs/op delta 160 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 161 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 162 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 163 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 164 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.000 n=10+10) 165 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.028 n=10+10) 166 | ``` 167 | ``` 168 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillFullDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillFullDequeQueue.txt 169 | name old time/op new time/op delta 170 | /1-4 3.41µs ± 0% 3.53µs ± 1% +3.53% (p=0.000 n=9+10) 171 | /10-4 34.2µs ± 1% 34.1µs ± 0% ~ (p=0.887 n=10+7) 172 | /100-4 334µs ± 1% 333µs ± 1% ~ (p=0.095 n=10+9) 173 | /1000-4 3.33ms ± 1% 3.32ms ± 1% -0.44% (p=0.040 n=9+9) 174 | /10000-4 36.9ms ± 0% 35.3ms ± 1% -4.38% (p=0.000 n=9+8) 175 | /100000-4 376ms ± 0% 387ms ± 1% +3.06% (p=0.000 n=8+9) 176 | 177 | name old alloc/op new alloc/op delta 178 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 179 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 180 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 181 | /1000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.016 n=10+10) 182 | /10000-4 30.1MB ± 0% 23.1MB ± 0% -23.16% (p=0.000 n=10+10) 183 | /100000-4 320MB ± 0% 241MB ± 0% -24.70% (p=0.000 n=10+10) 184 | 185 | name old allocs/op new allocs/op delta 186 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 187 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 188 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 189 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 190 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.001 n=9+10) 191 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.000 n=8+9) 192 | ``` 193 | ``` 194 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowIncreaseDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowIncreaseDequeQueue.txt 195 | name old time/op new time/op delta 196 | /1-4 234ns ±21% 192ns ± 0% -18.01% (p=0.000 n=10+10) 197 | /10-4 1.14µs ± 4% 1.03µs ± 0% -10.13% (p=0.000 n=9+9) 198 | /100-4 7.83µs ± 1% 7.28µs ± 0% -7.06% (p=0.000 n=10+9) 199 | /1000-4 69.3µs ± 1% 67.4µs ± 1% -2.78% (p=0.000 n=10+10) 200 | /10000-4 688µs ± 0% 688µs ± 6% ~ (p=0.190 n=9+9) 201 | /100000-4 8.11ms ± 1% 7.55ms ± 2% -7.00% (p=0.000 n=9+10) 202 | /1000000-4 82.1ms ± 1% 82.6ms ± 1% +0.58% (p=0.004 n=10+9) 203 | 204 | name old alloc/op new alloc/op delta 205 | /1-4 208B ± 0% 176B ± 0% -15.38% (p=0.000 n=10+10) 206 | /10-4 1.78kB ± 0% 1.10kB ± 0% -37.84% (p=0.000 n=10+10) 207 | /100-4 8.80kB ± 0% 6.08kB ± 0% -30.91% (p=0.000 n=10+10) 208 | /1000-4 54.2kB ± 0% 43.3kB ± 0% -20.14% (p=0.000 n=10+10) 209 | /10000-4 487kB ± 0% 405kB ± 0% -16.95% (p=0.000 n=10+8) 210 | /100000-4 4.82MB ± 0% 4.02MB ± 0% -16.62% (p=0.000 n=10+8) 211 | /1000000-4 48.2MB ± 0% 40.2MB ± 0% -16.60% (p=0.000 n=10+10) 212 | 213 | name old allocs/op new allocs/op delta 214 | /1-4 5.00 ± 0% 5.00 ± 0% ~ (all equal) 215 | /10-4 25.0 ± 0% 25.0 ± 0% ~ (all equal) 216 | /100-4 207 ± 0% 207 ± 0% ~ (all equal) 217 | /1000-4 2.02k ± 0% 2.02k ± 0% ~ (all equal) 218 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 219 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 220 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 221 | ``` 222 | ``` 223 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowDecreaseDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowDecreaseDequeQueue.txt 224 | name old time/op new time/op delta 225 | /1-4 35.8ns ± 4% 34.9ns ± 2% -2.66% (p=0.000 n=9+9) 226 | /10-4 375ns ± 6% 352ns ± 1% -6.22% (p=0.000 n=10+9) 227 | /100-4 3.60µs ± 2% 3.43µs ± 1% -4.58% (p=0.000 n=9+10) 228 | /1000-4 36.2µs ± 5% 34.2µs ± 1% -5.40% (p=0.000 n=10+9) 229 | /10000-4 364µs ± 8% 342µs ± 1% -6.00% (p=0.000 n=9+10) 230 | /100000-4 3.51ms ± 1% 3.42ms ± 0% -2.43% (p=0.000 n=10+9) 231 | /1000000-4 35.2ms ± 1% 34.2ms ± 1% -2.65% (p=0.000 n=10+9) 232 | 233 | name old alloc/op new alloc/op delta 234 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 235 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 236 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 237 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 238 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 239 | /100000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.011 n=10+10) 240 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% -0.00% (p=0.011 n=10+10) 241 | 242 | name old allocs/op new allocs/op delta 243 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 244 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 245 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 246 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 247 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 248 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 249 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 250 | ``` 251 | ``` 252 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkStableDequeQueuev1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkStableDequeQueue.txt 253 | name old time/op new time/op delta 254 | /1-4 33.4ns ± 2% 34.2ns ± 1% +2.18% (p=0.002 n=9+9) 255 | /10-4 339ns ± 1% 345ns ± 2% +1.77% (p=0.000 n=9+10) 256 | /100-4 3.45µs ± 6% 3.34µs ± 1% ~ (p=0.122 n=10+8) 257 | /1000-4 33.1µs ± 1% 33.5µs ± 1% +1.12% (p=0.001 n=9+10) 258 | /10000-4 332µs ± 1% 335µs ± 0% +0.72% (p=0.004 n=10+8) 259 | /100000-4 3.30ms ± 1% 3.35ms ± 1% +1.34% (p=0.000 n=10+10) 260 | /1000000-4 33.1ms ± 1% 33.5ms ± 1% +1.06% (p=0.006 n=10+9) 261 | 262 | name old alloc/op new alloc/op delta 263 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 264 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 265 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 266 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 267 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 268 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (p=0.982 n=10+10) 269 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% ~ (p=0.065 n=10+10) 270 | 271 | name old allocs/op new allocs/op delta 272 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 273 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 274 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 275 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 276 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 277 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 278 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 279 | ``` 280 | 281 | ### LIFO stack 282 | ``` 283 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillDequeStack.txt 284 | name old time/op new time/op delta 285 | /1-4 4.08µs ±15% 3.98µs ±14% ~ (p=0.367 n=10+9) 286 | /10-4 36.3µs ± 4% 37.5µs ±22% ~ (p=0.968 n=9+10) 287 | /100-4 350µs ± 3% 389µs ±21% +11.04% (p=0.003 n=8+10) 288 | /1000-4 3.49ms ± 3% 3.28ms ± 1% -5.99% (p=0.000 n=10+9) 289 | /10000-4 38.0ms ± 5% 34.6ms ± 1% -9.09% (p=0.000 n=10+9) 290 | /100000-4 397ms ± 2% 387ms ± 1% -2.55% (p=0.000 n=8+10) 291 | 292 | name old alloc/op new alloc/op delta 293 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 294 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 295 | /100-4 160kB ± 0% 160kB ± 0% -0.00% (p=0.000 n=10+10) 296 | /1000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.000 n=9+10) 297 | /10000-4 30.1MB ± 0% 23.1MB ± 0% -23.14% (p=0.000 n=8+10) 298 | /100000-4 320MB ± 0% 241MB ± 0% -24.71% (p=0.000 n=10+10) 299 | 300 | name old allocs/op new allocs/op delta 301 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 302 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 303 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 304 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 305 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.001 n=9+10) 306 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.001 n=10+10) 307 | ``` 308 | ``` 309 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillFullDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkRefillFullDequeStack.txt 310 | name old time/op new time/op delta 311 | /1-4 3.40µs ± 1% 3.48µs ± 1% +2.40% (p=0.000 n=10+10) 312 | /10-4 33.5µs ± 1% 33.6µs ± 1% ~ (p=0.720 n=10+9) 313 | /100-4 332µs ± 1% 326µs ± 0% -1.74% (p=0.000 n=8+9) 314 | /1000-4 3.31ms ± 1% 3.25ms ± 1% -1.77% (p=0.000 n=7+9) 315 | /10000-4 36.6ms ± 1% 34.8ms ± 1% -4.87% (p=0.000 n=9+10) 316 | /100000-4 371ms ± 0% 384ms ± 1% +3.46% (p=0.000 n=9+9) 317 | 318 | name old alloc/op new alloc/op delta 319 | /1-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 320 | /10-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 321 | /100-4 160kB ± 0% 160kB ± 0% ~ (all equal) 322 | /1000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.003 n=10+9) 323 | /10000-4 30.1MB ± 0% 23.1MB ± 0% -23.14% (p=0.000 n=10+9) 324 | /100000-4 320MB ± 0% 241MB ± 0% -24.71% (p=0.000 n=9+10) 325 | 326 | name old allocs/op new allocs/op delta 327 | /1-4 100 ± 0% 100 ± 0% ~ (all equal) 328 | /10-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 329 | /100-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 330 | /1000-4 100k ± 0% 100k ± 0% ~ (all equal) 331 | /10000-4 1.01M ± 0% 1.01M ± 0% -0.00% (p=0.033 n=10+10) 332 | /100000-4 10.1M ± 0% 10.1M ± 0% -0.00% (p=0.000 n=9+10) 333 | ``` 334 | ``` 335 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowIncreaseDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowIncreaseDequeStack.txt 336 | name old time/op new time/op delta 337 | /1-4 198ns ± 0% 198ns ± 2% ~ (p=0.493 n=10+10) 338 | /10-4 925ns ±10% 908ns ± 1% ~ (p=0.481 n=9+8) 339 | /100-4 8.35µs ± 5% 7.60µs ± 2% -9.06% (p=0.000 n=10+10) 340 | /1000-4 70.1µs ± 3% 68.7µs ± 1% -1.92% (p=0.003 n=10+9) 341 | /10000-4 722µs ± 3% 715µs ± 1% ~ (p=0.083 n=10+8) 342 | /100000-4 8.08ms ± 3% 7.98ms ±11% ~ (p=0.143 n=10+10) 343 | /1000000-4 81.0ms ± 1% 83.7ms ± 2% +3.35% (p=0.000 n=10+10) 344 | 345 | name old alloc/op new alloc/op delta 346 | /1-4 208B ± 0% 176B ± 0% -15.38% (p=0.000 n=10+10) 347 | /10-4 752B ± 0% 592B ± 0% -21.28% (p=0.000 n=10+10) 348 | /100-4 8.80kB ± 0% 6.08kB ± 0% -30.91% (p=0.000 n=10+10) 349 | /1000-4 50.0kB ± 0% 41.2kB ± 0% -17.72% (p=0.000 n=10+10) 350 | /10000-4 483kB ± 0% 403kB ± 0% -16.67% (p=0.000 n=8+10) 351 | /100000-4 4.82MB ± 0% 4.02MB ± 0% -16.62% (p=0.000 n=10+9) 352 | /1000000-4 48.2MB ± 0% 40.2MB ± 0% -16.60% (p=0.000 n=10+10) 353 | 354 | name old allocs/op new allocs/op delta 355 | /1-4 5.00 ± 0% 5.00 ± 0% ~ (all equal) 356 | /10-4 24.0 ± 0% 24.0 ± 0% ~ (all equal) 357 | /100-4 207 ± 0% 207 ± 0% ~ (all equal) 358 | /1000-4 2.01k ± 0% 2.01k ± 0% ~ (all equal) 359 | /10000-4 20.1k ± 0% 20.1k ± 0% ~ (all equal) 360 | /100000-4 201k ± 0% 201k ± 0% ~ (all equal) 361 | /1000000-4 2.01M ± 0% 2.01M ± 0% ~ (all equal) 362 | ``` 363 | ``` 364 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowDecreaseDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkSlowDecreaseDequeStack.txt 365 | name old time/op new time/op delta 366 | /1-4 35.2ns ± 1% 34.7ns ± 1% -1.44% (p=0.000 n=10+10) 367 | /10-4 356ns ± 1% 353ns ± 1% -0.63% (p=0.019 n=8+10) 368 | /100-4 3.48µs ± 0% 3.42µs ± 0% -1.69% (p=0.000 n=8+8) 369 | /1000-4 34.8µs ± 1% 34.0µs ± 1% -2.19% (p=0.000 n=9+10) 370 | /10000-4 348µs ± 1% 340µs ± 1% -2.38% (p=0.000 n=9+10) 371 | /100000-4 3.55ms ± 5% 3.41ms ± 1% -3.87% (p=0.000 n=10+9) 372 | /1000000-4 35.4ms ± 7% 34.2ms ± 1% -3.56% (p=0.000 n=9+9) 373 | 374 | name old alloc/op new alloc/op delta 375 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 376 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 377 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 378 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 379 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 380 | /100000-4 1.60MB ± 0% 1.60MB ± 0% -0.00% (p=0.023 n=8+9) 381 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% -0.00% (p=0.041 n=10+10) 382 | 383 | name old allocs/op new allocs/op delta 384 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 385 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 386 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 387 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 388 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 389 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 390 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 391 | ``` 392 | ``` 393 | benchstat ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkStableDequeStackv1.0.3.txt ./../../../ef-ds/deque-bench-tests/testdata/BenchmarkStableDequeStack.txt 394 | name old time/op new time/op delta 395 | /1-4 32.8ns ± 1% 33.7ns ± 1% +2.72% (p=0.000 n=10+8) 396 | /10-4 332ns ± 1% 339ns ± 1% +2.19% (p=0.000 n=9+10) 397 | /100-4 3.24µs ± 1% 3.33µs ± 2% +2.82% (p=0.000 n=8+9) 398 | /1000-4 33.2µs ± 7% 32.5µs ± 1% ~ (p=0.360 n=10+8) 399 | /10000-4 322µs ± 2% 325µs ± 1% +0.69% (p=0.034 n=10+8) 400 | /100000-4 3.23ms ± 1% 3.26ms ± 1% +1.05% (p=0.001 n=10+10) 401 | /1000000-4 32.2ms ± 1% 32.5ms ± 1% +0.84% (p=0.003 n=10+10) 402 | 403 | name old alloc/op new alloc/op delta 404 | /1-4 16.0B ± 0% 16.0B ± 0% ~ (all equal) 405 | /10-4 160B ± 0% 160B ± 0% ~ (all equal) 406 | /100-4 1.60kB ± 0% 1.60kB ± 0% ~ (all equal) 407 | /1000-4 16.0kB ± 0% 16.0kB ± 0% ~ (all equal) 408 | /10000-4 160kB ± 0% 160kB ± 0% ~ (all equal) 409 | /100000-4 1.60MB ± 0% 1.60MB ± 0% ~ (p=0.127 n=10+10) 410 | /1000000-4 16.0MB ± 0% 16.0MB ± 0% -0.00% (p=0.018 n=10+10) 411 | 412 | name old allocs/op new allocs/op delta 413 | /1-4 1.00 ± 0% 1.00 ± 0% ~ (all equal) 414 | /10-4 10.0 ± 0% 10.0 ± 0% ~ (all equal) 415 | /100-4 100 ± 0% 100 ± 0% ~ (all equal) 416 | /1000-4 1.00k ± 0% 1.00k ± 0% ~ (all equal) 417 | /10000-4 10.0k ± 0% 10.0k ± 0% ~ (all equal) 418 | /100000-4 100k ± 0% 100k ± 0% ~ (all equal) 419 | /1000000-4 1.00M ± 0% 1.00M ± 0% ~ (all equal) 420 | ``` 421 | -------------------------------------------------------------------------------- /unit_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2018 ef-ds 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in all 11 | // copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | // SOFTWARE. 20 | 21 | package deque 22 | 23 | import ( 24 | "fmt" 25 | "testing" 26 | ) 27 | 28 | const ( 29 | refillCount = 3 30 | pushCount = maxInternalSliceSize * 3 // Push to fill at least 3 internal slices 31 | ) 32 | 33 | func TestNewShouldReturnInitiazedInstanceOfDeque(t *testing.T) { 34 | d := New[interface{}]() 35 | assertInvariants(t, d, nil) 36 | } 37 | 38 | func TestInvariantsWhenEmptyInMiddleOfSlice(t *testing.T) { 39 | d := new(Deque[interface{}]) 40 | d.PushBack(0) 41 | assertInvariants(t, d, nil) 42 | d.PushBack(1) 43 | assertInvariants(t, d, nil) 44 | d.PopFront() 45 | assertInvariants(t, d, nil) 46 | d.PopFront() 47 | // At this point, the queue is empty and hp will 48 | // not be pointing at the start of the slice. 49 | assertInvariants(t, d, nil) 50 | } 51 | 52 | func TestPushFrontPopBackShouldHaveAllInternalLinksInARing(t *testing.T) { 53 | d := New[interface{}]() 54 | pushValue, extraAddedItems, spareLinks := 0, 0, 0 55 | 56 | // Push maxFirstSliceSize items to fill the first array 57 | expectedHeadSliceSize := firstSliceSize 58 | for i := 1; i <= maxFirstSliceSize; i++ { 59 | pushValue++ 60 | d.PushFront(pushValue) 61 | 62 | checkLinks(t, d, pushValue, expectedHeadSliceSize, expectedHeadSliceSize, spareLinks, d.head, d.head, d.head, d.head) 63 | if pushValue >= expectedHeadSliceSize { 64 | expectedHeadSliceSize *= sliceGrowthFactor 65 | } 66 | } 67 | 68 | // Push 1 extra item to force the creation of a new array 69 | pushValue++ 70 | d.PushFront(pushValue) 71 | extraAddedItems++ 72 | checkLinks(t, d, pushValue, maxInternalSliceSize, maxFirstSliceSize, spareLinks, d.tail, d.tail, d.head, d.head) 73 | 74 | // Push another maxInternalSliceSize-1 to fill the second array 75 | for i := 1; i <= maxInternalSliceSize-1; i++ { 76 | pushValue++ 77 | d.PushFront(pushValue) 78 | checkLinks(t, d, pushValue, maxInternalSliceSize, maxFirstSliceSize, spareLinks, d.tail, d.tail, d.head, d.head) 79 | } 80 | 81 | // Push 1 extra item to force the creation of a new array (3 total) 82 | pushValue++ 83 | d.PushFront(pushValue) 84 | checkLinks(t, d, pushValue, maxInternalSliceSize, maxFirstSliceSize, spareLinks, d.tail.p, d.tail, d.head, d.head.n) 85 | /// Check middle links 86 | if d.head.n.n != d.tail { 87 | t.Error("Expected: d.head.n.n == d.tail; Got: d.head.n.n != d.tail") 88 | } 89 | if d.head.n.p != d.head { 90 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 91 | } 92 | 93 | // Check final len after all pushes 94 | if d.Len() != maxFirstSliceSize+maxInternalSliceSize+extraAddedItems { 95 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize+maxInternalSliceSize+extraAddedItems, d.Len()) 96 | } 97 | 98 | // Pop maxFirstSliceSize-1 items to empty the tail slice 99 | popValue := 1 100 | for i := 1; i <= maxFirstSliceSize-1; i++ { 101 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 102 | t.Errorf("Expected: %d; Got: %d", popValue, v) 103 | } 104 | popValue++ 105 | checkLinks(t, d, pushValue-popValue+extraAddedItems, maxInternalSliceSize, maxFirstSliceSize, spareLinks, d.tail.p, d.tail, d.head, d.head.n) 106 | /// Check middle links 107 | if d.head.n.n != d.tail { 108 | t.Error("Expected: d.head.n.n == d.tail; Got: d.head.n.n != d.tail") 109 | } 110 | if d.head.n.p != d.head { 111 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 112 | } 113 | } 114 | 115 | // Pop one extra item to force moving the tail to the middle slice. This also means the old tail 116 | // slice should have no items now, so spareLinks should be increased. 117 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 118 | t.Errorf("Expected: %d; Got: %d", popValue, v) 119 | } 120 | spareLinks++ 121 | popValue++ 122 | checkLinks(t, d, pushValue-popValue+extraAddedItems, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 123 | //Check last slice links (not tail anymore; tail is the middle one) 124 | if d.head.p.n != d.head { 125 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 126 | } 127 | if d.head.p.p != d.tail { 128 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 129 | } 130 | 131 | // Pop maxInternalSliceSize-1 items to empty the tail (middle) slice 132 | for i := 1; i <= maxInternalSliceSize-1; i++ { 133 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 134 | t.Errorf("Expected: %d; Got: %d", popValue, v) 135 | } 136 | popValue++ 137 | checkLinks(t, d, pushValue-popValue+extraAddedItems, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 138 | //Check last slice links (not tail anymore; tail is the middle one) 139 | if d.head.p.n != d.head { 140 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 141 | } 142 | if d.head.p.p != d.tail { 143 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 144 | } 145 | } 146 | 147 | // Pop one extra item to force moving the tail to the head slice. This also means the old tail 148 | // slice should have no items now, so spareLinks should be increased. 149 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 150 | t.Errorf("Expected: %d; Got: %d", popValue, v) 151 | } 152 | spareLinks++ 153 | checkLinks(t, d, pushValue-popValue, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.tail.p.p, d.tail.p) 154 | /// Check middle links 155 | if d.head.n.n != d.tail.p { 156 | t.Error("Expected: d.head.n.n == d.tail.p; Got: d.head.n.n != d.tail.p") 157 | } 158 | if d.head.n.p != d.head { 159 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 160 | } 161 | //Check last slice links (not tail anymore; tail is the first one) 162 | if d.head.p.n != d.head { 163 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 164 | } 165 | if d.head.p.p != d.tail.n { 166 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 167 | } 168 | 169 | // Pop one extra item emptying the deque 170 | popValue++ 171 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 172 | t.Errorf("Expected: %d; Got: %d", popValue, v) 173 | } 174 | checkLinks(t, d, pushValue-popValue, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.tail.p.p, d.tail.p) 175 | /// Check middle links 176 | if d.head.n.n != d.tail.p { 177 | t.Error("Expected: d.head.n.n == d.tail.p; Got: d.head.n.n != d.tail.p") 178 | } 179 | if d.head.n.p != d.head { 180 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 181 | } 182 | //Check last slice links (not tail anymore; tail is the first one) 183 | if d.head.p.n != d.head { 184 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 185 | } 186 | if d.head.p.p != d.tail.n { 187 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 188 | } 189 | 190 | // The deque shoud be empty 191 | if d.Len() != 0 { 192 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 193 | } 194 | if _, ok := d.Back(); ok { 195 | t.Error("Expected: false; Got: true") 196 | } 197 | if _, ok := d.Front(); ok { 198 | t.Error("Expected: false; Got: true") 199 | } 200 | if len(d.head.v) != maxInternalSliceSize { 201 | t.Errorf("Expected: %d; Got: %d", maxInternalSliceSize, len(d.head.v)) 202 | } 203 | if len(d.tail.v) != maxInternalSliceSize { 204 | t.Errorf("Expected: %d; Got: %d", maxInternalSliceSize, len(d.tail.v)) 205 | } 206 | if d.head != d.tail { 207 | t.Error("Expected: d.head == d.tail; Got: d.head != d.tail") 208 | } 209 | if d.tp != maxInternalSliceSize-1 { 210 | t.Errorf("Expected: %d; Got: %d", 0, d.tp) 211 | } 212 | if d.hp != maxInternalSliceSize-1 { 213 | t.Errorf("Expected: %d; Got: %d", 0, d.hp) 214 | } 215 | if d.spareLinks != spareLinks { 216 | t.Errorf("Expected: %d; Got: %d", spareLinks, d.spareLinks) 217 | } 218 | } 219 | 220 | func TestPushFrontPopFrontShouldHaveAllInternalLinksInARing(t *testing.T) { 221 | d := New[interface{}]() 222 | pushValue, spareLinks := 0, 0 223 | 224 | // Push maxFirstSliceSize + maxInternalSliceSize + 1 items to fill the first, second 225 | // and have one item in the third slice 226 | for i := 1; i <= maxFirstSliceSize+maxInternalSliceSize+1; i++ { 227 | pushValue++ 228 | d.PushFront(pushValue) 229 | } 230 | 231 | // Pop one item to force moving the head to the middle slice. This also means the old head 232 | // slice should have no items now, so spareLinks should be increased. 233 | popValue := pushValue 234 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 235 | t.Errorf("Expected: %d; Got: %d", popValue, v) 236 | } 237 | spareLinks++ 238 | popValue-- 239 | checkLinks(t, d, popValue, maxInternalSliceSize, maxFirstSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 240 | //Check first slice links (not head anymore; head is the middle one) 241 | if d.head.p.n != d.head { 242 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 243 | } 244 | if d.head.p.p != d.tail { 245 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 246 | } 247 | 248 | // Pop maxFirstSliceSize-1 items to empty the head, middle slice 249 | for i := 1; i <= maxInternalSliceSize-1; i++ { 250 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 251 | t.Errorf("Expected: %d; Got: %d", popValue, v) 252 | } 253 | popValue-- 254 | checkLinks(t, d, popValue, maxInternalSliceSize, maxFirstSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 255 | /// Check first slice links 256 | if d.head.p.n != d.head { 257 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 258 | } 259 | if d.head.p.p != d.tail { 260 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 261 | } 262 | } 263 | 264 | // Pop one extra item to force moving the head to the last slice. This also means the old head 265 | // slice should have no items now, so spareLinks should be increased. 266 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 267 | t.Errorf("Expected: %d; Got: %d", popValue, v) 268 | } 269 | spareLinks++ 270 | popValue-- 271 | checkLinks(t, d, popValue, maxFirstSliceSize, maxFirstSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.head.p.p, d.head.p) 272 | //Check first slice links 273 | if d.head.p.p.n != d.head.p { 274 | t.Error("Expected: d.head.p.p.n == d.head.p; Got: d.head.p.p.n != d.head.p") 275 | } 276 | if d.head.p.p.p != d.tail { 277 | t.Error("Expected: d.head.p.p.p == d.tail; Got: d.head.p.p.p != d.tail") 278 | } 279 | //Check middle slice links 280 | if d.head.p.n != d.head { 281 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 282 | } 283 | if d.head.p.p != d.tail.n { 284 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 285 | } 286 | 287 | // Pop maxFirstSliceSize-1 items to empty the head/tail (last) slice 288 | for i := 1; i <= maxFirstSliceSize; i++ { 289 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 290 | t.Errorf("Expected: %d; Got: %d", popValue, v) 291 | } 292 | popValue-- 293 | checkLinks(t, d, popValue, maxFirstSliceSize, maxFirstSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.head.p.p, d.head.p) 294 | //Check first slice links 295 | if d.head.p.p.n != d.head.p { 296 | t.Error("Expected: d.head.p.p.n == d.head.p; Got: d.head.p.p.n != d.head.p") 297 | } 298 | if d.head.p.p.p != d.tail { 299 | t.Error("Expected: d.head.p.p.p == d.tail; Got: d.head.p.p.p != d.tail") 300 | } 301 | //Check middle slice links 302 | if d.head.p.n != d.head { 303 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 304 | } 305 | if d.head.p.p != d.tail.n { 306 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 307 | } 308 | } 309 | 310 | // The deque shoud be empty 311 | if d.Len() != 0 { 312 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 313 | } 314 | if _, ok := d.Back(); ok { 315 | t.Error("Expected: false; Got: true") 316 | } 317 | if _, ok := d.Front(); ok { 318 | t.Error("Expected: false; Got: true") 319 | } 320 | if len(d.head.v) != maxFirstSliceSize { 321 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize, len(d.head.v)) 322 | } 323 | if len(d.tail.v) != maxFirstSliceSize { 324 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize, len(d.tail.v)) 325 | } 326 | if d.head != d.tail { 327 | t.Error("Expected: d.head == d.tail; Got: d.head != d.tail") 328 | } 329 | if d.tp != maxFirstSliceSize-1 { 330 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize-1, d.tp) 331 | } 332 | if d.hp != maxFirstSliceSize-1 { 333 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize-1, d.hp) 334 | } 335 | if d.spareLinks != spareLinks { 336 | t.Errorf("Expected: %d; Got: %d", spareLinks, d.spareLinks) 337 | } 338 | } 339 | 340 | func TestPushBackPopBackShouldHaveAllInternalLinksInARing(t *testing.T) { 341 | d := New[interface{}]() 342 | pushValue, extraAddedItems, spareLinks := 0, 0, 0 343 | 344 | // Push maxFirstSliceSize items to fill the first array 345 | expectedHeadSliceSize := firstSliceSize 346 | for i := 1; i <= maxFirstSliceSize; i++ { 347 | pushValue++ 348 | d.PushBack(pushValue) 349 | 350 | checkLinks(t, d, pushValue, expectedHeadSliceSize, expectedHeadSliceSize, spareLinks, d.head, d.head, d.head, d.head) 351 | if pushValue >= expectedHeadSliceSize { 352 | expectedHeadSliceSize *= sliceGrowthFactor 353 | } 354 | } 355 | 356 | // Push 1 extra item to force the creation of a new array 357 | pushValue++ 358 | d.PushBack(pushValue) 359 | extraAddedItems++ 360 | checkLinks(t, d, pushValue, maxFirstSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail, d.head, d.head) 361 | 362 | // Push another maxInternalSliceSize-1 to fill the second array 363 | for i := 1; i <= maxInternalSliceSize-1; i++ { 364 | pushValue++ 365 | d.PushBack(pushValue) 366 | checkLinks(t, d, pushValue, maxFirstSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail, d.head, d.head) 367 | } 368 | 369 | // Push 1 extra item to force the creation of a new array (3 total) 370 | pushValue++ 371 | d.PushBack(pushValue) 372 | checkLinks(t, d, pushValue, maxFirstSliceSize, maxInternalSliceSize, spareLinks, d.tail.p, d.tail, d.head, d.head.n) 373 | /// Check middle links 374 | if d.head.n.n != d.tail { 375 | t.Error("Expected: d.head.n.n == d.tail; Got: d.head.n.n != d.tail") 376 | } 377 | if d.head.n.p != d.head { 378 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 379 | } 380 | 381 | // Check final len after all pushes 382 | if d.Len() != maxFirstSliceSize+maxInternalSliceSize+extraAddedItems { 383 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize+maxInternalSliceSize+extraAddedItems, d.Len()) 384 | } 385 | 386 | // Pop one item to force moving the tail to the middle slice. This also means the old tail 387 | // slice should have no items now, so spareLinks should be increased. 388 | popValue := d.Len() 389 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 390 | t.Errorf("Expected: %d; Got: %d", popValue, v) 391 | } 392 | spareLinks++ 393 | popValue-- 394 | checkLinks(t, d, popValue, maxFirstSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 395 | //Check last slice links (not tail anymore; tail is the middle one) 396 | if d.head.p.n != d.head { 397 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 398 | } 399 | if d.head.p.p != d.tail { 400 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 401 | } 402 | 403 | // Pop maxInternalSliceSize-1 items to empty the tail (middle) slice 404 | for i := 1; i <= maxInternalSliceSize-1; i++ { 405 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 406 | t.Errorf("Expected: %d; Got: %d", popValue, v) 407 | } 408 | popValue-- 409 | checkLinks(t, d, popValue, maxFirstSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 410 | /// Check last slice links 411 | if d.head.p.n != d.head { 412 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 413 | } 414 | if d.head.p.p != d.tail { 415 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 416 | } 417 | } 418 | 419 | // Pop one extra item to force moving the tail to the head (first) slice. This also means the old tail 420 | // slice should have no items now, so spareLinks should be increased. 421 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 422 | t.Errorf("Expected: %d; Got: %d", popValue, v) 423 | } 424 | popValue-- 425 | spareLinks++ 426 | checkLinks(t, d, popValue, maxFirstSliceSize, maxFirstSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.tail.p.p, d.tail.p) 427 | /// Check middle links 428 | if d.head.n.n != d.tail.p { 429 | t.Error("Expected: d.head.n.n == d.tail.p; Got: d.head.n.n != d.tail.p") 430 | } 431 | if d.head.n.p != d.head { 432 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 433 | } 434 | //Check last slice links (not tail anymore; tail is the first one) 435 | if d.head.p.n != d.head { 436 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 437 | } 438 | if d.head.p.p != d.tail.n { 439 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 440 | } 441 | 442 | // Pop maxFirstSliceSize-1 items to empty the head (first) slice 443 | for i := 1; i <= maxFirstSliceSize; i++ { 444 | if v, ok := d.PopBack(); !ok || v.(int) != popValue { 445 | t.Errorf("Expected: %d; Got: %d", popValue, v) 446 | } 447 | popValue-- 448 | checkLinks(t, d, popValue, maxFirstSliceSize, maxFirstSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.tail.p.p, d.tail.p) 449 | /// Check middle links 450 | if d.head.n.n != d.tail.p { 451 | t.Error("Expected: d.head.n.n == d.tail.p; Got: d.head.n.n != d.tail.p") 452 | } 453 | if d.head.n.p != d.head { 454 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 455 | } 456 | //Check last slice links (not tail anymore; tail is the first one) 457 | if d.head.p.n != d.head { 458 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 459 | } 460 | if d.head.p.p != d.tail.n { 461 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 462 | } 463 | } 464 | 465 | // The deque shoud be empty 466 | if d.Len() != 0 { 467 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 468 | } 469 | if _, ok := d.Back(); ok { 470 | t.Error("Expected: false; Got: true") 471 | } 472 | if _, ok := d.Front(); ok { 473 | t.Error("Expected: false; Got: true") 474 | } 475 | if len(d.head.v) != maxFirstSliceSize { 476 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize, len(d.head.v)) 477 | } 478 | if len(d.tail.v) != maxFirstSliceSize { 479 | t.Errorf("Expected: %d; Got: %d", maxFirstSliceSize, len(d.tail.v)) 480 | } 481 | if d.head.n == d.tail.p { 482 | t.Error("Expected: d.head.n != d.tail.p; Got: d.head.n == d.tail.p") 483 | } 484 | if d.tp != 0 { 485 | t.Errorf("Expected: %d; Got: %d", -1, d.tp) 486 | } 487 | if d.hp != 0 { 488 | t.Errorf("Expected: %d; Got: %d", 0, d.hp) 489 | } 490 | if d.spareLinks != spareLinks { 491 | t.Errorf("Expected: %d; Got: %d", spareLinks, d.spareLinks) 492 | } 493 | } 494 | 495 | func TestPushBackPopFrontShouldHaveAllInternalLinksInARing(t *testing.T) { 496 | d := New[interface{}]() 497 | pushValue, spareLinks := 0, 0 498 | 499 | // Push maxFirstSliceSize + maxInternalSliceSize + 1 items to fill the first, second 500 | // and have one item in the third slice 501 | for i := 1; i <= maxFirstSliceSize+maxInternalSliceSize+1; i++ { 502 | pushValue++ 503 | d.PushBack(pushValue) 504 | } 505 | 506 | // Pop maxFirstSliceSize-1 items to empty the head slice 507 | popValue := 0 508 | for i := 1; i <= maxFirstSliceSize-1; i++ { 509 | popValue++ 510 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 511 | t.Errorf("Expected: %d; Got: %d", popValue, v) 512 | } 513 | checkLinks(t, d, pushValue-popValue, maxFirstSliceSize, maxInternalSliceSize, spareLinks, d.tail.p, d.tail, d.head, d.head.n) 514 | /// Check middle slice links 515 | if d.head.n.n != d.tail { 516 | t.Error("Expected: d.head.n.n == d.tail; Got: d.head.n.n != d.tail") 517 | } 518 | if d.head.n.p != d.head { 519 | t.Error("Expected: d.head.n.p == d.head; Got: d.head.n.p != d.head") 520 | } 521 | } 522 | 523 | // Pop one item to force moving the head to the middle slice. This also means the old head 524 | // slice should have no items now, so spareLinks should be increased. 525 | popValue++ 526 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 527 | t.Errorf("Expected: %d; Got: %d", popValue, v) 528 | } 529 | spareLinks++ 530 | checkLinks(t, d, pushValue-popValue, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 531 | //Check first slice links (not head anymore; head is the middle one) 532 | if d.head.p.n != d.head { 533 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 534 | } 535 | if d.head.p.p != d.tail { 536 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 537 | } 538 | 539 | // Pop maxInternalSliceSize-1 items to empty the head (middle) slice 540 | for i := 1; i <= maxInternalSliceSize-1; i++ { 541 | popValue++ 542 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 543 | t.Errorf("Expected: %d; Got: %d", popValue, v) 544 | } 545 | checkLinks(t, d, pushValue-popValue, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail, d.tail.n, d.head.p, d.head) 546 | //Check first slice links 547 | if d.head.p.n != d.head { 548 | t.Error("Expected: d.head.p.n == d.head; Got: d.head.p.n != d.head") 549 | } 550 | if d.head.p.p != d.tail { 551 | t.Error("Expected: d.head.p.p == d.tail; Got: d.head.p.p != d.tail") 552 | } 553 | } 554 | 555 | // Pop one extra item making head move to next (last slice). This also means the old head 556 | // slice should have no items now, so spareLinks should be increased. 557 | popValue++ 558 | spareLinks++ 559 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 560 | t.Errorf("Expected: %d; Got: %d", popValue, v) 561 | } 562 | checkLinks(t, d, pushValue-popValue, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.head.p.p, d.head.p) 563 | //Check first slice links 564 | if d.head.n.n != d.head.p { 565 | t.Error("Expected: d.head.n.n == d.head.p; Got: d.head.n.n != d.head.p") 566 | } 567 | if d.head.n.p != d.tail { 568 | t.Error("Expected: d.head.n.p == d.tail; Got: d.head.n.p != d.tail") 569 | } 570 | //Check middle slice links 571 | if d.head.p.n != d.tail { 572 | t.Error("Expected: d.head.p.n == d.tail; Got: d.head.p.n != d.tail") 573 | } 574 | if d.head.p.p != d.tail.n { 575 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 576 | } 577 | 578 | // Pop one extra item emptying the deque. 579 | popValue++ 580 | if v, ok := d.PopFront(); !ok || v.(int) != popValue { 581 | t.Errorf("Expected: %d; Got: %d", popValue, v) 582 | } 583 | checkLinks(t, d, pushValue-popValue, maxInternalSliceSize, maxInternalSliceSize, spareLinks, d.tail.p.p, d.tail.p, d.head.p.p, d.head.p) 584 | //Check first slice links 585 | if d.head.n.n != d.head.p { 586 | t.Error("Expected: d.head.n.n == d.head.p; Got: d.head.n.n != d.head.p") 587 | } 588 | if d.head.n.p != d.tail { 589 | t.Error("Expected: d.head.n.p == d.tail; Got: d.head.n.p != d.tail") 590 | } 591 | //Check middle slice links 592 | if d.head.p.n != d.tail { 593 | t.Error("Expected: d.head.p.n == d.tail; Got: d.head.p.n != d.tail") 594 | } 595 | if d.head.p.p != d.tail.n { 596 | t.Error("Expected: d.head.p.p == d.tail.n; Got: d.head.p.p != d.tail.n") 597 | } 598 | 599 | // The deque shoud be empty 600 | if d.Len() != 0 { 601 | t.Errorf("Expected: %d; Got: %d", 0, d.Len()) 602 | } 603 | if _, ok := d.Back(); ok { 604 | t.Error("Expected: false; Got: true") 605 | } 606 | if _, ok := d.Front(); ok { 607 | t.Error("Expected: false; Got: true") 608 | } 609 | if len(d.head.v) != maxInternalSliceSize { 610 | t.Errorf("Expected: %d; Got: %d", maxInternalSliceSize, len(d.head.v)) 611 | } 612 | if len(d.tail.v) != maxInternalSliceSize { 613 | t.Errorf("Expected: %d; Got: %d", maxInternalSliceSize, len(d.tail.v)) 614 | } 615 | if d.head.p != d.tail.p { 616 | t.Error("Expected: d.head.p == d.tail.p; Got: d.head.p != d.tail.p") 617 | } 618 | if d.tp != 1 { 619 | t.Errorf("Expected: %d; Got: %d", 0, d.tp) 620 | } 621 | if d.hp != 1 { 622 | t.Errorf("Expected: %d; Got: %d", 1, d.hp) 623 | } 624 | if d.spareLinks != spareLinks { 625 | t.Errorf("Expected: %d; Got: %d", spareLinks, d.spareLinks) 626 | } 627 | } 628 | 629 | func TestPushFrontShouldReuseSpareLinks(t *testing.T) { 630 | d := New[interface{}]() 631 | count := maxInternalSliceSize * 3 632 | // Fills the deque 633 | for i := 0; i < count; i++ { 634 | d.PushFront(i) 635 | } 636 | // Pop the items to generate spare links 637 | for i := 0; i < maxInternalSliceSize; i++ { 638 | d.PopFront() 639 | } 640 | if d.spareLinks != 1 { 641 | t.Errorf("Expected: %d; Got: %d", 1, d.spareLinks) 642 | } 643 | 644 | // Push the items back using PushFront 645 | for i := 0; i < count; i++ { 646 | d.PushFront(i) 647 | } 648 | 649 | // The spare links should've been used up 650 | if d.spareLinks != 0 { 651 | t.Errorf("Expected: %d; Got: %d", 0, d.spareLinks) 652 | } 653 | } 654 | 655 | func TestPushFrontPopBackStableShouldReuseHeadSlice(t *testing.T) { 656 | d := New[interface{}]() 657 | 658 | for i := 0; i < pushCount; i++ { 659 | d.PushFront(i) 660 | v, ok := d.PopBack() 661 | if !ok || v == nil || v.(int) != i { 662 | t.Errorf("Expected: %d; Got: %d", i, v) 663 | } 664 | } 665 | 666 | // The head slice should've been reused, so no additional slices should've been created 667 | if d.head != d.tail || d.head.n != d.tail || d.head.p != d.tail { 668 | t.Error("Expected to have only one head slice") 669 | } 670 | if d.spareLinks != 0 { 671 | t.Errorf("Expected: %d spareLinks; Got: %d", maxSpareLinks, d.spareLinks) 672 | } 673 | } 674 | 675 | func TestPushBackShouldReuseSpareLinks(t *testing.T) { 676 | d := New[interface{}]() 677 | count := maxInternalSliceSize * 3 678 | // Fills the deque 679 | for i := 0; i < count; i++ { 680 | d.PushBack(i) 681 | } 682 | // Pop the items to generate spare links 683 | for i := 0; i < maxInternalSliceSize; i++ { 684 | d.PopBack() 685 | } 686 | if d.spareLinks != 1 { 687 | t.Errorf("Expected: %d; Got: %d", 1, d.spareLinks) 688 | } 689 | 690 | // Push the items back using PushFront 691 | for i := 0; i < count; i++ { 692 | d.PushBack(i) 693 | } 694 | 695 | // The spare links should've been used up 696 | if d.spareLinks != 0 { 697 | t.Errorf("Expected: %d; Got: %d", 0, d.spareLinks) 698 | } 699 | } 700 | 701 | func TestPopFrontWithRefillShouldKeepMaxSpareLinks(t *testing.T) { 702 | d := New[interface{}]() 703 | count := maxInternalSliceSize * (maxSpareLinks + 2) 704 | for i := 0; i < refillCount; i++ { 705 | for j := 0; j < count; j++ { 706 | d.PushBack(j) 707 | } 708 | for j := 0; j < count; j++ { 709 | if v, ok := d.PopFront(); !ok || v.(int) != j { 710 | t.Errorf("Expected: %d; Got: %d", j, v) 711 | } 712 | if d.spareLinks > maxSpareLinks { 713 | t.Fatalf("Too many spare links : got %d; want <= %d", d.spareLinks, maxSpareLinks) 714 | } 715 | } 716 | 717 | // Count the actual number of spare links 718 | actualSpareLinks := 0 719 | tmp := d.head.n // Initially head and tail points to the same slice. 720 | for tmp != nil { 721 | if tmp != d.tail { 722 | actualSpareLinks++ 723 | } else { 724 | break 725 | } 726 | tmp = tmp.n 727 | } 728 | if actualSpareLinks != maxSpareLinks { 729 | t.Errorf("Expected: %d; Got: %d", d.spareLinks, actualSpareLinks) 730 | } 731 | } 732 | } 733 | 734 | func TestPopBackWithRefillShouldKeepMaxSpareLinks(t *testing.T) { 735 | d := New[interface{}]() 736 | count := maxInternalSliceSize * (maxSpareLinks + 2) 737 | for i := 0; i < refillCount; i++ { 738 | for j := 0; j < count; j++ { 739 | d.PushFront(j) 740 | } 741 | for j := 0; j < count; j++ { 742 | if v, ok := d.PopBack(); !ok || v.(int) != j { 743 | t.Errorf("Expected: %d; Got: %d", j, v) 744 | } 745 | if d.spareLinks > maxSpareLinks { 746 | t.Fatalf("Too many spare links : got %d; want <= %d", d.spareLinks, maxSpareLinks) 747 | } 748 | } 749 | 750 | // Count the actual number of spare links 751 | actualSpareLinks := 0 752 | tmp := d.head.n // Initially head and tail points to the same slice. 753 | for tmp != nil { 754 | if tmp != d.tail { 755 | actualSpareLinks++ 756 | } else { 757 | break 758 | } 759 | tmp = tmp.n 760 | } 761 | if actualSpareLinks != maxSpareLinks { 762 | t.Errorf("Expected: %d; Got: %d", maxSpareLinks, actualSpareLinks) 763 | } 764 | } 765 | } 766 | 767 | // Helper methods----------------------------------------------------------------------------------- 768 | 769 | // Checks the internal slices and its links. 770 | func checkLinks(t *testing.T, d *Deque[interface{}], length, headSliceSize, tailSliceSize, spareLinks int, headNext, headPrevious, tailNext, tailPrevious *node[interface{}]) { 771 | t.Helper() 772 | if d.Len() != length { 773 | t.Errorf("unexpected length; Expected: %d; Got: %d", length, d.Len()) 774 | } 775 | if len(d.head.v) != headSliceSize { 776 | t.Errorf("unexpected head size; Expected: %d; Got: %d", headSliceSize, len(d.head.v)) 777 | } 778 | if d.head.n != headNext { 779 | t.Error("unexpected head node; Expected: d.head.n == headNext; Got: d.head.n != headNext") 780 | } 781 | if d.head.p != headPrevious { 782 | t.Error("unexpected head; Expected: d.head.p == headPrevious; Got: d.head.p != headPrevious") 783 | } 784 | if d.tail.n != tailNext { 785 | t.Error("unexpected tailNext; Expected: d.tail.n == tailNext; Got: d.tail.n != tailNext") 786 | } 787 | if d.tail.p != tailPrevious { 788 | t.Error("unexpected tailPrevious; Expected: d.tail.p == tailPrevious; Got: d.tail.p != tailPrevious") 789 | } 790 | if len(d.tail.v) != tailSliceSize { 791 | t.Errorf("unexpected tail size; Expected: %d; Got: %d", tailSliceSize, len(d.tail.v)) 792 | } 793 | if d.spareLinks != spareLinks { 794 | t.Errorf("unexpected spare link count; Expected: %d; Got: %d", spareLinks, d.spareLinks) 795 | } 796 | if t.Failed() { 797 | t.FailNow() 798 | } 799 | } 800 | 801 | // assertInvariants checks all the invariant conditions in d that we can think of. 802 | // If val is non-nil it is used to find the expected value for an item at index 803 | // i measured from the head of the queue. 804 | func assertInvariants(t *testing.T, d *Deque[interface{}], val func(i int) interface{}) { 805 | t.Helper() 806 | fail := func(what string, got, want interface{}) { 807 | t.Errorf("invariant fail: %s; got %v want %v", what, got, want) 808 | } 809 | if d == nil { 810 | fail("non-nil Deque", d, "non-nil") 811 | } 812 | if d.head == nil { 813 | // Zero value. 814 | if d.tail != nil { 815 | fail("nil tail when zero", d.tail, nil) 816 | } 817 | if d.len != 0 { 818 | fail("zero length when zero", d.len, 0) 819 | } 820 | if d.hp != 0 { 821 | fail("zero hp when zero", d.hp, 0) 822 | } 823 | if d.tp != 0 { 824 | fail("zero tp when zero", d.tp, 0) 825 | } 826 | if d.spareLinks != 0 { 827 | fail("no spare links when zero", d.spareLinks, 0) 828 | } 829 | return 830 | } 831 | if d.hp < 0 { 832 | fail("head index non-negative", d.hp, 0) 833 | } 834 | if d.hp > len(d.head.v) { 835 | fail("head index out of range", d.hp, len(d.head.v)) 836 | } 837 | if d.tp < 0 { 838 | fail("tail index non-negative", d.tp, 0) 839 | } 840 | if d.tp > len(d.tail.v) { 841 | fail("head index out of range", d.tp, len(d.tail.v)) 842 | } 843 | 844 | if d.head == d.tail { 845 | if d.tp-d.hp != d.len { 846 | fail("tail index - head index == len", d.tp-d.hp, d.len) 847 | } 848 | } 849 | spareLinkCount := 0 850 | inQueue := true 851 | elemCount := 0 852 | smallNodeCount := 0 853 | index := 0 854 | walkLinks(t, d, func(n *node[interface{}]) { 855 | if len(n.v) < maxInternalSliceSize { 856 | smallNodeCount++ 857 | if len(n.v) > maxFirstSliceSize { 858 | fail("first node within bounds", len(n.v), maxFirstSliceSize) 859 | } 860 | } 861 | if len(n.v) > maxInternalSliceSize { 862 | fail("slice too big", len(n.v), maxInternalSliceSize) 863 | } 864 | for i, v := range n.v { 865 | failElem := func(what string, got, want interface{}) { 866 | fail(fmt.Sprintf("at elem %d, node %p, %s", i, n, what), got, want) 867 | t.FailNow() 868 | } 869 | if !inQueue { 870 | if v != nil { 871 | failElem("all values outside queue nil", v, nil) 872 | } 873 | continue 874 | } 875 | elemInQueue := inQueue 876 | switch { 877 | case n == d.head && n == d.tail: 878 | elemInQueue = i >= d.hp && i < d.tp 879 | case n == d.head: 880 | elemInQueue = i >= d.hp 881 | case n == d.tail: 882 | elemInQueue = i < d.tp 883 | } 884 | if elemInQueue { 885 | if v == nil { 886 | failElem("all values inside queue non-nil", v, "non-nil") 887 | } 888 | } else { 889 | if v != nil { 890 | failElem("all values outside queue nil", v, nil) 891 | } 892 | } 893 | if v != nil { 894 | if val != nil { 895 | want := val(index) 896 | if want != v { 897 | failElem(fmt.Sprintf("element %d has expected value", index), v, want) 898 | } 899 | } 900 | elemCount++ 901 | index++ 902 | } 903 | } 904 | if !inQueue { 905 | spareLinkCount++ 906 | } 907 | if n == d.tail { 908 | inQueue = false 909 | } 910 | }) 911 | if inQueue { 912 | // We never encountered the tail pointer. 913 | t.Errorf("tail does not point to element in list") 914 | } 915 | if spareLinkCount > maxSpareLinks { 916 | fail("spare link count <= maxSpareLinks", spareLinkCount, maxSpareLinks) 917 | } 918 | if elemCount != d.len { 919 | fail("element count == d.len", elemCount, d.len) 920 | } 921 | if smallNodeCount > 1 { 922 | fail("only one first node", smallNodeCount, 1) 923 | } 924 | if t.Failed() { 925 | t.FailNow() 926 | } 927 | } 928 | 929 | // walkLinks calls f for each node in the linked list. 930 | // It also checks link invariants: 931 | func walkLinks(t *testing.T, d *Deque[interface{}], f func(n *node[interface{}])) { 932 | t.Helper() 933 | fail := func(what string, got, want interface{}) { 934 | t.Errorf("link invariant %s fail; got %v want %v", what, got, want) 935 | } 936 | n := d.head 937 | for { 938 | if n.n.p != n { 939 | fail("node.n.p == node", n.n.p, n) 940 | } 941 | if n.p.n != n { 942 | fail("node.p.n == node", n.p.n, n) 943 | } 944 | f(n) 945 | n = n.n 946 | if n == d.head { 947 | break 948 | } 949 | } 950 | } 951 | --------------------------------------------------------------------------------