# Mapslices and @threads

**URL:** <https://discourse.julialang.org/t/mapslices-and-threads/28138>\
**Category:** General Usage\
**Tags:** multithreading, mapslices\
**Created:** [August 28, 2019, 9:05pm UTC](https://discourse.julialang.org/t/mapslices-and-threads/28138 "2019-08-28T21:05:05Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Paul\_Soderlind](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_soderlind/32/1753_2.png) [@Paul\_Soderlind](https://discourse.julialang.org/u/Paul_Soderlind)\
**Post date:** [August 28, 2019, 9:05pm UTC](https://discourse.julialang.org/t/mapslices-and-threads/28138/1 "2019-08-28T21:05:06Z")

</div>

I am fooling around with 1.30-rc1 and would like to understand how to make a call on mapslices threaded.

Since mapslices can emulate a loop over an array dimension, it would nice to make that “loop” Threads.@threads. Does anyone have a hint?

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [August 28, 2019, 9:51pm UTC](https://discourse.julialang.org/t/mapslices-and-threads/28138/2 "2019-08-28T21:51:33Z")

</div>

Writing a loop over `eachcol(x)` is usually faster than `mapslices` even before you add threading. Or you can use my [SliceMap](https://github.com/mcabbott/SliceMap.jl), which has `tmapcols`. Or use [JuliennedArrays](https://github.com/bramtayl/JuliennedArrays.jl) for more complicated slicing.

---

<div class="post-metadata">

**Author:** ![Paul\_Soderlind](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paul_soderlind/32/1753_2.png) [@Paul\_Soderlind](https://discourse.julialang.org/u/Paul_Soderlind)\
**Post date:** [August 29, 2019, 10:21am UTC](https://discourse.julialang.org/t/mapslices-and-threads/28138/3 "2019-08-29T10:21:26Z")

</div>

My use case us that I want to write code that can take an `AbstractArray` as input and then do operations along one dimension. In some calls the array will be a `Matrix`, but in other cases there will be more dimensions.

This rules out `eachcol` and probably `SliceMap`, or? Not even `eachslice` seems to work well since it only allows a single dimension in `dims`.

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [August 29, 2019, 12:21pm UTC](https://discourse.julialang.org/t/mapslices-and-threads/28138/4 "2019-08-29T12:21:11Z")

</div>

If I understand right, then yes, `eachslice` allows only one “outer” dimension, meaning that your function will get `N-1`-dimensional arrays when acting on an `N`-array. Slightly confusingly `mapslices` takes the “inner” dimensions as `dims` instead, as do `Slices` and `slicemap`. But these should be more general:

```julia
julia> map(ndims, eachslice(ones(1,2,3), dims=3))
3-element Array{Int64,1}:
 2
 2
 2

julia> mapslices(ndims, ones(1,2,3), dims=(1,2))
1×1×3 Array{Int64,3}:
[:, :, 1] =
 2

[:, :, 2] =
 2

[:, :, 3] =
 2

julia> using JuliennedArrays

julia> map(ndims, Slices(ones(1,2,3), 1,2))
3-element Array{Int64,1}:
 2
 2
 2

julia> using SliceMap

julia> slicemap(ndims, ones(1,2,3), dims=(1,2))
3-element Array{Int64,1}:
 2
 2
 2

```

Here of course the function gives a scalar; had it made an array then the `map` examples would be arrays of arrays, needing `reduce(hcat,...` or `Align` etc, which is built into `mapslices` / `slicemap`.

---

<div class="post-metadata">

**Author:** ![kongdd](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kongdd/32/17058_2.png) [@kongdd](https://discourse.julialang.org/u/kongdd)\
**Post date:** [January 11, 2024, 4:57pm UTC](https://discourse.julialang.org/t/mapslices-and-threads/28138/5 "2024-01-11T16:57:17Z")

</div>

> [@Paul\_Soderlind](#):
>
> Threads

Here is my solution. Hope it helps.

> <https://github.com/jl-pkgs/Ipaper.jl/blob/master/src/par_mapslices.jl#L49>

````julia
import Base.Threads
import Base: Slice, concatenate_setindex!

"""
    par_mapslices(f, A::AbstractArray{<:Real,N}, args...; dims=N, kw...)

# Arguments
- `dims`: the dimension apply f

@seealso `mapslices`

# Example
```julia
A = rand(10, 10, 30, 4)
par_mapslices(mean, A)
```
"""
function par_mapslices(f, A::AbstractArray{<:Real,N}, args...; dims=N, kw...) where {N}
  idx1 = ntuple(d -> d in dims ? (:) : firstindex(A, d), ndims(A))
  Aslice = A[idx1...]
  r1 = f(Aslice)

  _dims = size(A) |> collect
  _dims[dims] = length(r1)

  dim_mask = ntuple(d -> d in dims, ndims(A))

  itershape = ntuple(d -> d in dims ? Base.OneTo(1) : axes(A, d), ndims(A))
  indices = CartesianIndices(itershape)

  R = zeros(eltype(r1), _dims...)

  slice_A = Slice.(axes(A))
  slice_R = Slice.(axes(R))

  @inbounds Threads.@threads for I in indices
    idx = ifelse.(dim_mask, slice_A, Tuple(I))
    ridx = ifelse.(dim_mask, slice_R, Tuple(I))

    # _unsafe_getindex!(Aslice, A, idx...)
    Aslice = @view A[idx...]
    r = f(Aslice, args...; kw...)
    concatenate_setindex!(R, r, ridx...)
  end
  R |> squeeze
end
```
````
