# \[ANN\] InplacePermutations.jl

**URL:** <https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570>\
**Category:** Package Announcements\
**Created:** [August 2, 2026, 4:28pm UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570 "2026-08-02T16:28:08Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [August 2, 2026, 4:28pm UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/1 "2026-08-02T16:28:08Z")

</div>

Hello,

This started as a fun exploration, I don’t have a specific usecase for it but maybe somebody else will.

This is a very small package that introduces a `permutedims!(array, perm)` variant that permutes an array reusing the same underlaying memory buffer without needing auxiliary buffers/allocations.  
It needs to known array sizes and permutation tuple at compile time for best performance.

```julia-auto
using InplacePermutations

A = rand(8, 6, 2, 2)
Aorig = copy(A) # keep a pristine reference for comparison

B = InplacePermutations.permutedims!(A, (2, 4, 1, 3)) # mutates A in place

B == permutedims(Aorig, (2, 4, 1, 3)) # true
size(B) # (6, 2, 8, 2)

```

Baseline `Base.permutedims!` with a `(36,24)` array

```julia-auto
BenchmarkTools.Trial: 10000 samples with 259 evaluations per sample.
 Range (min … max): 297.297 ns … 692.085 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 297.938 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 302.942 ns ± 15.778 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

  █▃▁ ▁ ▁▂▃▁ ▁
  ███████████▇▇██▇▆▇▇▆▅▆▆▅▅▅▅▅▅▆▅▅▅▅▅▅▅▄▆▅▅▆▆▄▆▆▆▅▅▆▅▄▅▅▅▅▅▄▅▄▃ █
  297 ns Histogram: log(frequency) by time 377 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

Same array with array size and permutation known at compile time

```julia-auto
BenchmarkTools.Trial: 10000 samples with 671 evaluations per sample.
 Range (min … max): 187.841 ns … 325.633 ns ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 188.462 ns ┊ GC (median): 0.00%
 Time (mean ± σ): 191.987 ns ± 8.242 ns ┊ GC (mean ± σ): 0.00% ± 0.00%

  █▆▂▁▁▁▂▄▂▁▁ ▁ ▁ ▁
  █████████████████▇▇██▇▇▇▇▆▆▆▆▇▆▇▆▇▇█▇▇▇▇▇▇▇▇▇▇▆▅▅▆▅▅▆▅▆▅▅▅▅▅▆ █
  188 ns Histogram: log(frequency) by time 226 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

```

There are some caveats:

- Works best on small ish arrays
- Can be compilation heavy with big arrays (tunable trade-off between compilation time and speed)
- New sizes/perm combinations will generate a new function everytime.

More details in the [repo](https://github.com/ghyatzo/InplacePermutations.jl).

PS.: currently being registered. Not married to the name, if you have opinions let me know!

---

<div class="post-metadata">

**Author:** ![greg\_plowman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/greg_plowman/32/8100_2.png) [@greg\_plowman](https://discourse.julialang.org/u/greg_plowman)\
**Post date:** [August 3, 2026, 12:36am UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/2 "2026-08-03T00:36:10Z")

</div>

Would `InplacePermuteDims` be a better name? Seems narrower and less suggestive of permutations in general?

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [August 3, 2026, 3:09am UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/3 "2026-08-03T03:09:33Z")

</div>

For Intel/AMD processors with AVX-512 and small arrays (up to 2048 bit), one could use the fast vector indexing from SmallCollections.jl, at least if the array size is known at compile time. As an example, here is an in-place transpose:

```julia-auto
julia> using InplacePermutations, StaticArrays, SmallCollections, Chairmarks

julia> a = rand(MMatrix{16,16,Int8});

julia> @b smalltranspose!($a), InplacePermutations.permutedims!($a, Val((2, 1)))
(11.503 ns, 66.851 ns)

julia> a = rand(MMatrix{17,15,Int8});

julia> @b smalltranspose!($a), InplacePermutations.permutedims!($a, Val((2, 1)))
(15.091 ns, 127.801 ns)

```

The 2048 bit limit is to avoid excessive code generation. I didn’t try to improve `smalltranspose!` for variable matrix sizes.

> **Code**
>
> ```julia-auto
> function smalltranspose!(a::AbstractMatrix{T}) where T
> N = length(a)
> @assert N <= 256
> p = Ptr{FixedVector{N,T}}(pointer(a))
> v = unsafe_load(p)
> u = FixedVector{N}(transpose(LinearIndices(a))) .% UInt8
> w = @inbounds v[u]
> unsafe_store!(p, w)
> reshape(a, axes(a, 2), axes(a, 1))
> end
> 
> ```

---

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [August 3, 2026, 6:38am UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/4 "2026-08-03T06:38:41Z")

</div>

Yeah you are probably right. Good call.

---

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [August 3, 2026, 6:51am UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/5 "2026-08-03T06:51:44Z")

</div>

This is cool! Although this is still allocating an intermediate array `w` correct? In this experiment I wanted to see if I could get rid of any intermediate buffer.

The purely in-place is definitely not the best way of doing this speed wise. As you just showed leveraging SIMD is much better and by construction my approach is definitely not very amenable to that.

I’m not sure if there’s ever a situation where you can’t spare the extra memory to allocate a destination array and this package could become useful. But just in case!

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [August 3, 2026, 12:25pm UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/6 "2026-08-03T12:25:36Z")

</div>

> [@cshen](#):
>
> this is still allocating an intermediate array `w` correct?

No, `w` is an allocation-free [`FixedVector`](https://matthias314.github.io/SmallCollections.jl/stable/fixedvector/#SmallCollections.FixedVector), analogous to `SVector`. (The `@b` macro would otherwise report an allocation.) For variable size one should probably use an allocation-free [`SmallVector`](https://matthias314.github.io/SmallCollections.jl/stable/smallvector/#SmallCollections.SmallVector) (with a maximal length).

---

<div class="post-metadata">

**Author:** ![cshen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cshen/32/217287_2.png) [@cshen](https://discourse.julialang.org/u/cshen)\
**Post date:** [August 3, 2026, 4:39pm UTC](https://discourse.julialang.org/t/ann-inplacepermutations-jl/138570/7 "2026-08-03T16:39:42Z")

</div>

I see, that’s super interesting, Thanks! I’ll definitely play a little bit with that library and see if we can merge the two approaches
