# Julep: Efficient Hierarchical Mutable Data

**URL:** <https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190>\
**Category:** Internals & Design\
**Tags:** mutable-structure, rfc\
**Created:** [January 25, 2025, 6:51am UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190 "2025-01-25T06:51:04Z")\
**Posts on this page:** 8\
**Page:** 3

<div class="post-metadata">

**Author:** ![cstjean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cstjean/32/1444_2.png) [@cstjean](https://discourse.julialang.org/u/cstjean)\
**Post date:** [February 3, 2025, 7:06am UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/42 "2025-02-03T07:06:57Z")

</div>

Come to think of it, sorry that I didn’t follow everything and maybe it’s been discussed, but

> [@PatrickHaecker](#):
>
> For isbits types, `StaticArrays` is great. However, most of the values in our data structures need to be modified during the real-time phase. This works well when `StructArrays` is appropriate, for small immutable leaf structures by creating a new instance, and for slightly larger types with `Accessors`.

Couldn’t that be made to work? `MArray` uses a tuple internally to get the flat memory layout, but then uses [unsafe\_store!](https://github.com/JuliaArrays/StaticArrays.jl/blob/89d8653c49d2ab2f90198c365b644af716b1b060/src/MArray.jl#L35) to guarantee that it’s modified efficiently. We can do the same for a struct. Something like

```julia
struct Elephant
     height::Int
end

mutable struct Forest::Mutable{(:elephant,)}
    elephant::Elephant
    name::String
end

f = Forest(Elephant(1), "Boya")
f.elephant.height = 10 # works thanks to getproperty returning a view

```

using a method like

```julia
@generated Base.getproperty(m::Mutable{T}, field) = 
     # check if field is in T, if so return a view whose `setproperty!` will use unsafe_store! to change the field

```

We’d still need complex views, but maybe no macro… It’s not so different from the macro proposal, but perhaps the type is more julian.

EDIT: fixed that `Forest` must be a `mutable`, of course.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [February 3, 2025, 12:53pm UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/43 "2025-02-03T12:53:21Z")

</div>

> [@cstjean](#):
>
> Couldn’t that be made to work? `MArray` uses a tuple internally to get the flat memory layout, but then uses [unsafe\_store!](https://github.com/JuliaArrays/StaticArrays.jl/blob/89d8653c49d2ab2f90198c365b644af716b1b060/src/MArray.jl#L35) to guarantee that it’s modified efficiently.

Afaiu MArray doesn’t really work for pointer types, because of the missing intrinsic `interior_pointerset` to apply the right write barrier (meaning – if you use MArray with e.g. strings, you can get a nice crash / memory corruption / UAF security vuln).

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [February 3, 2025, 1:30pm UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/44 "2025-02-03T13:30:39Z")

</div>

As @foobar_lv2 I also think that this cannot work, but wanted to provide the link to the discussion as I think this is the approach I linked to in the section [Earlier Attempts](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190#p-587913-earlier-attempts-5), where @andyferris had it implemented like this and then there was agreement, that it cannot work correctly in all cases (for non-`isbits` values) and he had to remove it again.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [February 3, 2025, 2:30pm UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/45 "2025-02-03T14:30:08Z")

</div>

> [@foobar\_lv2](#):
>
> meaning – if you use MArray with e.g. strings, you can get a nice crash / memory corruption / UAF security vuln

Strictly speaking you wouldn’t when (implicitly) using `setindex!`, as [this case nowadays throws an error](https://github.com/JuliaArrays/StaticArrays.jl/commit/0f3ed2b0ad3760d40fa95e030deaa2ffa9034060). You can test it with

```julia
using StaticArrays
julia> MVector{1, String}(undef)[1] = "42"
ERROR: setindex!() with non-isbitstype eltype is not supported by StaticArrays. Consider using SizedArray.

```

However, you can probably get all of what you mentioned when using `unsafe_store!`. This is probably what you meant, I just wanted to highlight, that `MArray` is safe as long as you do not use any `unsafe` function.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [February 3, 2025, 5:26pm UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/46 "2025-02-03T17:26:40Z")

</div>

> [@cstjean](#):
>
> I’m not quite up-to-date with the C FFI, but couldn’t we write a method of `pointer_from_objref(::ElephantView)` that returns, a pointer to `C.elephant`?

That’s a really good idea and can probably be generalized. We should go through the list of the shortcomings and see what we can improve. But let’s focus on the `pointer_from_objref` in this post.

It does work, but the first symbol of each substruct (the `__a` here) needs to be entered manually, which should be integrated into the macro:

```julia
includet("inline_struct.jl") # containing @cstjean's code above
abstract type AbstractPart end
mutable struct Part <: AbstractPart
    a::Float32
end
@with_inline_types mutable struct Whole2 
    b::Int32
    part1::@inline(Part)
    part2::@inline(Part) 
end

Base.fieldoffset(x::DataType, sym::Symbol) = fieldoffset(x, findfirst(sym |> ==, x |> fieldnames))

Base.pointer_from_objref(pv::PartView{S, W}) where {S, W} =
    (getfield(pv, :obj) |> pointer_from_objref) + fieldoffset(W, Symbol(S, "__a"))

julia> const w2 = Whole2(Int32(42), Part(42.0f0), Part(43.0f0));

julia> (w2, w2.part1, w2.part2) .|> pointer_from_objref
(Ptr{Nothing} @0x00007f8bb3d802b0, Ptr{Nothing} @0x00007f8bb3d802b4, Ptr{Nothing} @0x00007f8bb3d802b8)

```

> I wouldn’t start a package like this by myself, but if you want to start from the above code (or some other implementation), I’m down to co-maintain. Alternatively, I could put it inside of [QuickTypes.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/QuickTypes), where it’s somewhat thematically relevant.

While `QuickTypes.jl` indeed is related, I assume that there could be a lot of users using only the existing `QuickTypes.jl` functionality or the struct inlining discussed here. Therefore, a separate package looks cleaner to me.

Thanks for the offer to co-maintain. Let’s see how far we get here and how much I can get an understanding of your macro magic. This might really make sense.

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [February 4, 2025, 7:16am UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/47 "2025-02-04T07:16:58Z")

</div>

This even seems to work for inline arrays. We’ll need a macro implementation and we’ll need to see how it scales, but first results are very promising:

```julia
includet("inline_struct.jl")
abstract type AbstractInlineArray end
mutable struct InlineArray <: AbstractInlineArray
    a __1::Int8; a__ 2::Int8; a __3::Int8; a__ 4::Int8
    a __5::Int8; a__ 6::Int8; a __7::Int8; a__ 8::Int8
end
# Base.getindex(x::AbstractInlineArray, i) = getfield(x, Symbol("a__$i")) # ≈ 110 ns
function Base.getindex(a::AbstractInlineArray, i) # ≈ 2 ns
    i == 1 && return a.a__1
    i == 2 && return a.a__2
    i == 3 && return a.a__3
    i == 4 && return a.a__4
    i == 5 && return a.a__5
    i == 6 && return a.a__6
    i == 7 && return a.a__7
    i == 8 && return a.a__8
    BoundsError(a, i) |> throw
end
function Base.setindex!(a::AbstractInlineArray, val, i) # ≈ 2 ns
    i == 1 && return a.a__1 = val
    i == 2 && return a.a__2 = val
    i == 3 && return a.a__3 = val
    i == 4 && return a.a__4 = val
    i == 5 && return a.a__5 = val
    i == 6 && return a.a__6 = val
    i == 7 && return a.a__7 = val
    i == 8 && return a.a__8 = val
    BoundsError(a, i) |> throw
end
@with_inline_types mutable struct Whole3 
    b::Int32
    array::@inline(InlineArray)
end

julia> const w3 = Whole3(Int32(42), InlineArray(11, 12, 13, 14, 15, 16, 17, 18))
Whole3(42, 11, 12, 13, 14, 15, 16, 17, 18)

julia> w3.array[2] = Int8(22)
22

julia> w3
Whole3(42, 11, 22, 13, 14, 15, 16, 17, 18)

```

---

<div class="post-metadata">

**Author:** ![PatrickHaecker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/patrickhaecker/32/222891_2.png) [@PatrickHaecker](https://discourse.julialang.org/u/PatrickHaecker)\
**Post date:** [February 7, 2025, 5:01am UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/48 "2025-02-07T05:01:02Z")

</div>

> [@PatrickHaecker](#):
>
> This might mean that nothing can observe the originally created object which is the source object of the copy and that probably brings us back to the `new` approach.

I think I lumped too much here. Basically, we seem to have two approaches which me either might choose from or allow both. The semantics are fine if Julia can proof that …

- … only one `Part` object gets created and returned. Then the `new` call of `Part` should effectively be modified to reuse memory from `Whole`.
- … or the returned `Part` object did not escape. Then the `Whole` constructor can copy `p` into `w` after `p`’s creation when control flow returns to the `Whole` constructor without violating object identity (too much). Strictly speaking there could still be side effects, like conditional code execution based on `p's` memory address in the constructor of `Part`, but they seem exotic and can be addressed in the documentation.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 12, 2025, 4:54pm UTC](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190/49 "2025-02-12T16:54:12Z")

</div>

> [@nsajko](#):
>
> Alternatively, [FixedSizeArrays.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/FixedSizeArrays) could relax its `size` field from `NTuple{N,Int}` to any subtype of `NTuple{N,Integer}` (or maybe even go with `Number` instead of with `Integer`), then one of the various “static int”/“type domain int” types defined in various packages could be used to describe the size of any dimension.

PR here: [`size` field: allow `Integer` values of singleton type as elements by nsajko · Pull Request #107 · JuliaArrays/FixedSizeArrays.jl · GitHub](https://github.com/JuliaArrays/FixedSizeArrays.jl/pull/107)

[Previous page](https://discourse.julialang.org/t/julep-efficient-hierarchical-mutable-data/125190.md?page=2)
