# Automatic resizing of struct internal array when value changes?

**URL:** https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790
**Category:** Performance
**Created:** [January 30, 2023, 8:26pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790 "2023-01-30T20:26:38Z")
**Posts on this page:** 15
**Page:** 1

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 8:26pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/1 "2023-01-30T20:26:39Z")

</div>

Hello!

Suppose I have a struct as such:

```julia
mutable struct TheStruct
      N::Int
      x::AbstractVector{Float32}
end

```

And at different times “N” might change, for example from 5 to 7. When this happens I want to `resize!(x,N)` using the updated value of N = 7.

Is there a way to do this automatically, such there is some kind of “listener”?

Kind regards

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [January 30, 2023, 8:34pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/2 "2023-01-30T20:34:54Z")

</div>

Not in general, no. You can overload `setproperty!` to also `resize` the array when `N` changes, but that won’t prevent a determined user from bypassing your invariant using `setfield!`.

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 8:36pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/3 "2023-01-30T20:36:22Z")

</div>

That sounds good to me, I am doing this mostly to learn and give it a shot! I think it could be useful for me.

Would you happen to know how to write such an overload of `setproperty!`?

Kind regards

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [January 30, 2023, 8:42pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/4 "2023-01-30T20:42:01Z")

</div>

Both `getproperty` and `setproperty!` are kind of fiddly to get right, since they inherently rely on constant propagation of the accessed field for their type stability. You can write what you want to achieve like this:

```julia
function Base.setproperty!(ts::TheStruct, s::Symbol, v)
    if s === :N
        setfield!(ts, s, v) 
        arr = getfield(ts, :x) 
        resize!(arr, v)
    else
        setfield!(ts, s, v) 
    end
end

```

There’s probably a race condition here in multithreaded code. If I may ask, what are you trying to learn about using this?

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 8:45pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/5 "2023-01-30T20:45:48Z")

</div>

> [@Sukera](#):
>
> ```julia
> Base.setproperty!(ts::TheStruct, s::Symbol, v)
> if s === :N
> setfield!(ts, s, v) 
> arr = getfield(ts, :x) 
> resize!(arr, v)
> else
> setfield!(ts, s, v) 
> end
> 
> ```

I have a struct made as:

```julia
# Main Struct for Gradient Values
@with_kw struct ∇ᵢWᵢⱼStruct{T,ST}
    NL::Base.RefValue{Int}

    # Input for calculation
    xᵢⱼ ::AbstractVector{SVector{ST,T}} = zeros(SVector{ST,T},NL[])
    xᵢⱼ² ::AbstractVector{T} = similar(xᵢⱼ,T,NL[])
    dᵢⱼ ::AbstractVector{T} = similar(xᵢⱼ,T,NL[])
    qᵢⱼ ::AbstractVector{T} = similar(xᵢⱼ,T,NL[])
    ∇ᵢWᵢⱼ ::AbstractVector{SVector{ST,T}} = similar(xᵢⱼ,NL[])
end

```

Where NL is the variable determining the length of each array. When I update NL, I wish for all other arrays to update in size as well.

To me this would be neat if it was done through updating “NL” automatically 🙂

The reason I use BaseValue.Ref etc. is explained more here:

> [@How to properly pass structs into GPU? (MWE included)](https://discourse.julialang.org/t/how-to-properly-pass-structs-into-gpu-mwe-included/93727/7):
>
> Thank God, I was finally able to do it exactly how I wanted! And this answers my 5th question: 5. Is the way shown here the intended and “state of the art” approach for using structs in GPU programming - if not, what is? I looked at the interpolate example in the docs, and I struggle with grasping it. Short answer, no. The code below is in my opinion much better: # Packages using CUDA using Parameters using StaticArrays using Adapt using Setfield using LinearAlgebra using BenchmarkTools # Co…

So I was hoping that by updating NL I could trigger a “reaction” updating everything which is an AbstractArray in the struct to the new size

Kind regards

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [January 30, 2023, 9:02pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/6 "2023-01-30T21:02:28Z")

</div>

> [@Ahmed\_Salih](#):
>
> So I was hoping that by updating NL I could trigger a “reaction” updating everything which is an AbstractArray in the struct to the new size

You’ll have to implement that manually using `getproperty` - there is no builtin “callback” or the sort. If it’s about GPU code, I have my doubts about this being efficient though.

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 9:14pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/7 "2023-01-30T21:14:33Z")

</div>

I think I solved it!

This works for me:

```julia
function Base.setproperty!(ts::∇ᵢWᵢⱼStruct, s::Symbol, v)
    if s === :NL
        ts.NL[] = v
        for P in propertynames(ts)
            arr = getfield(ts, P)
            if isa(arr,AbstractVector)
                resize!(arr, v)
                fill!(arr,zero(eltype(arr)))
            end
        end
    else
        @warn "You are trying to setproperty! of immutable struct. Operation cancelled"
    end
end

```

```julia
@benchmark setproperty!($ts,$(:NL),$(10^5))
BenchmarkTools.Trial: 10000 samples with 1 evaluation.
 Range (min … max): 177.000 μs … 6.790 ms ┊ GC (min … max): 0.00% … 0.00%
 Time (median): 187.900 μs ┊ GC (median): 0.00%
 Time (mean ± σ): 218.299 μs ± 124.975 μs ┊ GC (mean ± σ): 0.00% ± 0.00%

  ▅█▅▃▂▃▃▁▁▁ ▁▁ ▁
  ███████████████████▇▇▆▆▆▆▇▆▅▅▆▆▆▅▅▅▅▆▅▆▅▅▅▅▄▅▆▅▅▄▅▄▄▄▅▅▃▃▅▄▄▄ █
  177 μs Histogram: log(frequency) by time 641 μs <

 Memory estimate: 3.91 KiB, allocs estimate: 95.

```

Looks good to you?

Kind regards

---

<div class="post-metadata">

### Author: ![dylanxyz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dylanxyz/32/36646_2.png) [@dylanxyz](https://discourse.julialang.org/u/dylanxyz)
#### Post date: [January 30, 2023, 9:14pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/8 "2023-01-30T21:14:42Z")

</div>

Why don’t you overload `Base.resize!` for your type?

I think this is more idiomatic:

```julia
function Base.resize!(object::TheStruct, N::Integer)
    object.N = N
    resize!(object.x, N)
    # do other things here
end

```

Then, you can just call `resize!` instead of manually updating `N` with the dot syntax.

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 9:20pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/9 "2023-01-30T21:20:25Z")

</div>

> [@dylanxyz](#):
>
> ```julia
> function Base.resize!(object::TheStruct, N::Integer)
> object.N = N
> resize!(object.x, (N,))
> # do other things here
> end
> 
> ```

I think this is the actual solution I was looking for! Thank you for realizing that for me.

And thank you @Sukera for showing me how I would be able to do it the way you showed me, even though it was quite “hacky”. Performance of both of them is very similar:

```julia
julia> @CUDA.time setproperty!(ts,:NL,10^5)
  0.001449 seconds (153 CPU allocations: 7.781 KiB) (5 GPU allocations: 3.433 MiB, 3.41% memmgmt time)

julia> @CUDA.time resize!(ts,10^5)
  0.001277 seconds (144 CPU allocations: 7.016 KiB) (5 GPU allocations: 3.433 MiB, 3.99% memmgmt time)

```

Resize wins out slightly.

I don’t get why it allocates though on the GPU, I am using in-place operations?

The code is:

```julia
function Base.resize!(object::∇ᵢWᵢⱼStruct, N::Integer)
    object.NL[] = N
    for P in propertynames(object)
        arr = getfield(ts, P)
        if isa(arr,AbstractVector)
            resize!(arr, N)
            fill!(arr,zero(eltype(arr)))
        end
    end
end

```

Kind regards

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [January 30, 2023, 9:27pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/10 "2023-01-30T21:27:03Z")

</div>

> [@Ahmed\_Salih](#):
>
> I don’t get why it allocates though on the GPU, I am using in-place operations?

Resizing may need to allocate a new array, if the new size is too large. This includes copying the old data into the new array.

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 9:28pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/11 "2023-01-30T21:28:54Z")

</div>

Okay, thank you, I thought I would just change the length of the container and then it would not need to allocate any new memory…

Will live with this for now and see if I can avoid it some way later 🙂

Kind regards

---

<div class="post-metadata">

### Author: ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)
#### Post date: [January 30, 2023, 9:37pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/12 "2023-01-30T21:37:23Z")

</div>

I think that to guarantee no allocations you have to allocate a “big enough” vector and then hand out slices. Otherwise `resize!` may need to allocate more memory, I am not sure if `resize!` ever decides to do it when downsizing, so maybe you could just use `resize!` if you already start at the largest size possible, but I am not sure.

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 9:45pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/13 "2023-01-30T21:45:44Z")

</div>

Okay, I think I like that idea!

So what you are in a sense proposing;

1. If I know that N is at max 120k, then I preallocate this size of array
2. In my overloaded resize operation, I instead return the “valid indices” for this step of my calculation
3. If the (new N) \> (old N) then I do the allocation of memory of course

So in this way I will only need to allocate memory when I actually need it and the values above “current N” are “trash”.

I really like this approach since I also just found out that I can set the values of the valid indices using a @view to get 0 GPU allocs:

```julia
@CUDA.time fill!(@view(V_GPU_Kernel.dᵢⱼ[1:50000]),100)
0.001678 seconds (60 CPU allocations: 3.688 KiB)

# And then

V_GPU_Kernel.dᵢⱼ
100000-element CuArray{Float32, 1, CUDA.Mem.DeviceBuffer}:
 100.0
 100.0
 100.0
 100.0
 100.0
 100.0
 100.0
 100.0
 100.0
 100.0
 100.0
   ⋮
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0
   0.0

```

---

<div class="post-metadata">

### Author: ![Henrique\_Becker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/henrique_becker/32/15443_2.png) [@Henrique\_Becker](https://discourse.julialang.org/u/Henrique_Becker)
#### Post date: [January 30, 2023, 10:26pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/14 "2023-01-30T22:26:11Z")

</div>

Yes, this is what I was proposing. If views work for your application, and keeping the maximum size allocated is not a problem, then it is a good solution.

I do not understand how GPU programming work, so just to be clear:

1. `resize!` often allocates more than the `n` you give it, so future resizes will not need to re-allocate and copy, but you only have access to indices up to `n` (in C++ vectors this is the difference between `size` and `capacity`).
2. `resize!` does this re-allocation transparently, the `Vector` just gets bigger, it is the same object in Julia and you do not need to adapt nothing in your side, but the underlying C pointer may be pointing to a different memory region and low-level libraries/GPU maybe can have a problem with this, I do not know.

---

<div class="post-metadata">

### Author: ![Ahmed\_Salih](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ahmed_salih/32/206579_2.png) [@Ahmed\_Salih](https://discourse.julialang.org/u/Ahmed_Salih)
#### Post date: [January 30, 2023, 10:43pm UTC](https://discourse.julialang.org/t/automatic-resizing-of-struct-internal-array-when-value-changes/93790/15 "2023-01-30T22:43:41Z")

</div>

Great!

I finished the first implementation, I added a “MaxValidIndex” field to my struct:

```julia
@with_kw struct ∇ᵢWᵢⱼStruct{T,ST}
    NL::Base.RefValue{Int}
    MaxValidIndex::Base.RefValue{Int} = deepcopy(NL)

    # Input for calculation
    xᵢⱼ ::AbstractVector{SVector{ST,T}} = zeros(SVector{ST,T},NL[])
    xᵢⱼ² ::AbstractVector{T} = similar(xᵢⱼ,T,NL[])
    dᵢⱼ ::AbstractVector{T} = similar(xᵢⱼ,T,NL[])
    qᵢⱼ ::AbstractVector{T} = similar(xᵢⱼ,T,NL[])
    ∇ᵢWᵢⱼ ::AbstractVector{SVector{ST,T}} = similar(xᵢⱼ,NL[])
end

```

And the resize! function is now as follows:

```julia
function Base.resize!(object::∇ᵢWᵢⱼStruct, N::Integer)
    if N > object.NL[]
        object.NL[] = N
        for P in propertynames(object)
            arr = getfield(object, P)
            if isa(arr,AbstractVector)
                resize!(arr, N)
                fill!(arr,zero(eltype(arr)))
            end
        end
    else
        object.MaxValidIndex[] = N
        for P in propertynames(object)
            arr = getfield(object, P)
            if P !== :xᵢⱼ
                if isa(arr,AbstractVector)
                    fill!(@view(arr[1:N]),zero(eltype(arr)))
                end
            end
        end
        
    end

    return nothing
end

```

So basically if the new N is suffient to be held in the old array, we do not allocate a new one, but instead “clean” values up to new N (MaxValidIndex). All my functions have now been changed to respect MaxValidIndex and I know that the range from 1:MaxValidIndex will always hold the real values, everything above is “trash”.

This also did not give me any noticeable regression in performance so this is awesome. Thank you very much @Henrique_Becker for the idea, since re-allocating all the time would be devasting for my code, i.e imagine 200k iterations with 5 mb realloc each time… now limited

EDIT: Also regarding your point 1 and 2. I noticed that if I increased size of GPU array with 1, it would still allocate a whole new one. It does not seem to be “smart” in the background as on CPU

Kind regards
