Skip to main content

Key findings

  • Slice with preallocation is the fastest option across all operations (Enqueue, Dequeue, Push, Pop) on both amd64 and arm64.
  • Preallocating always helps. Passing a sizeHint to the constructor eliminates growth allocations in the steady state and improves throughput in every case.
  • Rankings are consistent across Go 1.18 and Go 1.24, and across amd64 and arm64 architectures. The relative ordering of implementations does not change with Go version or CPU architecture.
  • The overall ranking from fastest to slowest: Slice > ListWithInternalPool > List > ListWithSyncPool

Benchmark data

ARM64 (Apple M3 Pro, Go 1.24.1)

AMD64 (Intel Core i7-4980HQ @ 2.80GHz, Go 1.18.0)

Ranking totals sum the per-operation rank across all four operations. A lower total means better overall throughput. >> in the per-operation rankings below indicates a large performance drop.

Per-operation ranking

Performance tips

Always provide a size hint

Passing a sizeHint avoids repeated backing-array growth and is the single highest-impact tuning you can do. Even a rough estimate is better than 0.

Prefer slice-based implementations

Unless you have profiled a specific allocation bottleneck, start with the slice-backed implementation. On both amd64 and arm64, across Go 1.18 and Go 1.24, it consistently outperforms all linked-list variants when preallocated.

Avoid sync.Pool-based implementations

ListWithSyncPool (ListSP) ranks last in nearly every benchmark. The sync.Pool GC interaction and coordination overhead outweighs any allocation savings for typical queue and stack workloads.
Do not switch to ListWithSyncPool without first profiling your application and confirming a measurable improvement. Benchmark data shows it is consistently the slowest option.

Set and OrderedMap have no alternatives to tune

Only one implementation exists for each:
  • Set: set.NewBasicMap — map-backed, no alternative implementations.
  • OrderedMap: orderedmap.NewSlice — slice-backed, no alternative implementations.
Both are well-optimized for general use. Providing a size hint is still the primary lever available to you.

Running your own benchmarks

Run benchmarks against the queue and stack packages:
Run benchmarks on your own hardware and Go version. While rankings are consistent across architectures and Go versions, absolute ns/op values vary by CPU and runtime. Use your own measurements to set concrete size hints and capacity targets for your specific environment.