-
Notifications
You must be signed in to change notification settings - Fork 14
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve performance of eachslice
#34
Comments
In 1.1, is this sufficient
Or do we want to have the |
We want the slice to have names too. But that is not hard, I don't think. Trick to me is working out how to do it in 1.0. |
Fixing Compat.jl is the better way to do this... But if that's loads of work, we could just copy the |
I do not know how to get allocations down on something like this function Base.eachslice(a::NamedDimsArray; dims, kwargs...)
numerical_dims = dim(a, dims)
data = Base.eachslice(parent(a); dims=numerical_dims, kwargs...)
new_names = slice_names(a, numerical_dims)
return Base.Generator(NamedDimsArray{new_names}, data)
end
function slice_names(a::NamedDimsArray{L}, n::Integer) where L
return compile_time_return_hack((L[1:n-1]..., L[n+1:end]...))
end julia> a = rand(30, 30, 30);
julia> nda = NamedDimsArray(a, (:a, :b, :c));
julia> @btime (() -> eachslice($a; dims=3))();
185.928 ns (3 allocations: 64 bytes)
julia> @btime (() -> eachslice($nda; dims=:c))();
4.917 μs (13 allocations: 544 bytes)
I am not sure where the allocations come from... julia> @btime Base.Generator(identity, eachslice($a; dims=3));
257.180 ns (4 allocations: 80 bytes) But julia> @btime (() -> NamedDims.slice_names($nda, 1))();
3.659 μs (3 allocations: 160 bytes) |
You should be able to use Rather than your new |
wow - very useful - and i was not about to figure out the hack that makes that function not allocate thanks |
but unfortunately swapping in function Base.eachslice(a::NamedDimsArray{L}; dims, kwargs...) where L
numerical_dims = dim(a, dims)
data = eachslice(parent(a); dims=numerical_dims, kwargs...)
new_names = remaining_dimnames_after_dropping(L, numerical_dims)
return Base.Generator(NamedDimsArray{new_names}, data)
end julia> @btime (() -> eachslice($nda; dims=:c))();
4.258 μs (13 allocations: 560 bytes) |
Here's where the allocations come from # `eachslice` by itself allocates 3 times
julia> function Base.eachslice(a::NamedDimsArray{L}; dims, kwargs...) where L
numerical_dims = NamedDims.dim(a, dims)
data = Base.eachslice(parent(a); dims=numerical_dims, kwargs...)
return data
end
julia> @btime (() -> eachslice($nda; dims=:c))();
199.451 ns (3 allocations: 64 bytes)
# somehow computing `new_names` adds 3
julia> function Base.eachslice(a::NamedDimsArray{L}; dims, kwargs...) where L
numerical_dims = NamedDims.dim(a, dims)
data = Base.eachslice(parent(a); dims=numerical_dims, kwargs...)
new_names = NamedDims.remaining_dimnames_after_dropping(L, numerical_dims)
return data
end
julia> @btime (() -> eachslice($nda; dims=:c))();
3.111 μs (6 allocations: 240 bytes)
# the `Generator` call adds 1
julia> function Base.eachslice(a::NamedDimsArray{L}; dims, kwargs...) where L
numerical_dims = NamedDims.dim(a, dims)
data = Base.eachslice(parent(a); dims=numerical_dims, kwargs...)
new_names = NamedDims.remaining_dimnames_after_dropping(L, numerical_dims)
return Base.Generator(identity, data)
end
julia> @btime (() -> eachslice($nda; dims=:c))();
3.191 μs (7 allocations: 256 bytes)
# passing the `NamedDimsArray` constructor adds 6
julia> function Base.eachslice(a::NamedDimsArray{L}; dims, kwargs...) where L
numerical_dims = NamedDims.dim(a, dims)
data = Base.eachslice(parent(a); dims=numerical_dims, kwargs...)
new_names = NamedDims.remaining_dimnames_after_dropping(L, numerical_dims)
return Base.Generator(NamedDimsArray{new_names}, data)
end
julia> @btime (() -> eachslice($nda; dims=:c))();
4.143 μs (13 allocations: 560 bytes) |
Got an order of magnitude improvement,
|
I had a look at this. Why define a custom iterator for
In both cases, |
@nickrobinson251 is tracking this better than I am |
I'm not sure I follow completely... but absolutely make a PR if you've a way to get the same |
We should add
eachslice
in julia 1.1 this can be an overload for
Base.eachslice
and in earlier we can just export it.
Right now can kinda fake it via;
vec(mapslices(nda; dim=dim))
Example
The text was updated successfully, but these errors were encountered: