Note that your code also allocates if we add @noinline to foo and @code_llvm foo(bla) shows that a call to jl_gc_pool_alloc is being generated.
Consider this slightly longer example:
struct Bla
x::Int
end
@noinline function Base.iterate(iter::Bla, state)
st, idx = state
if idx <= length(st)
return st[idx], (st, idx + 1)
else
return nothing
end
end
@noinline function Base.iterate(iter::Bla)
st = collect(1:iter.x)
idx = 1
return Base.iterate(iter, (st, idx))
end
function sumslow(bla::Bla)
n = 0
for x in bla
n += x
end
return n
end
Base.IteratorSize(::Type{Bla}) = Base.SizeUnknown()
Base.eltype(::Type{Bla}) = Int
The @noinline is intentional since this is a reduced example and I do not want to force @inline for the project I am working on.
I get the following result:
julia> @btime sumslow(Bla(2^20))
12.555 ms (1048578 allocations: 40.00 MiB)
549756338176
Without the @noinline, I get
julia> @btime sumslow(Bla(2^20))
1.950 ms (2 allocations: 8.00 MiB)
549756338176