Slices are amazing, and they’re allocated on the stack, but they do allocate GC-managed memory. If you’re interested in working entirely on the stack, you can try using StaticArrays. They are completely allocated on the stack with no heap memory at all and might save you a bit of CPU time, as well. Here’s a quick benchmark of allocating a bunch of one vs the other:
Example code here
require "benchmark"
# Assigning to values declared outside the block so the block
# executions don't get optimized away
slice = Slice(Int32).new(10, 42)
array = StaticArray(Int32, 10).new(42)
Benchmark.ips do |x|
x.report "slice" { slice = Slice(Int32).new(512, 42) }
x.report "static array" { array = StaticArray(Int32, 512).new(42) }
end
# We also sometimes need to use the values after so the assignment
# doesn't get optimized away :-)
p slice.size
p array.size
slice 1.72M (579.85ns) (± 2.77%) 2.01kB/op 2.49× slower
static array 4.29M (233.06ns) (± 1.17%) 0.0B/op fastest
Static arrays aren’t perfect and if you’re passing them heavily between methods it you might have better performance with slices because of time spent in memcpy, but if you spend a significant amount of time in GC, they might be worthwhile.