# Memory \`isbits\` Union Optimization

**URL:** https://discourse.julialang.org/t/memory-isbits-union-optimization/126175
**Category:** Performance
**Tags:** array, arrays
**Created:** [February 22, 2025, 7:10am UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175 "2025-02-22T07:10:56Z")
**Posts on this page:** 6
**Page:** 1

<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 22, 2025, 7:10am UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175/1 "2025-02-22T07:10:56Z")

</div>

Citing from [isbits union memory optimization](https://docs.julialang.org/en/v1/devdocs/isbitsunionarrays/#isbits-Union-Memory) (emphasis mine)

> The optimization is accomplished by storing an extra “type tag memory” of bytes, _one byte per element_, alongside the bytes of the actual data.

However, I see a memory usage of two bytes per element:

```julia
julia> sizeandsummary(x) = x |> Ref .|> (sizeof, Base.summarysize)

julia> 1:100 |> Memory{UInt8} |> sizeandsummary
(100, 116)

julia> 1:100 |> Memory{Union{Nothing,UInt8}} |> sizeandsummary
(200, 316)

```

Why is this the case? Given that in this example only one bit is needed for the type tag, two bytes seem excessive.

---

<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 22, 2025, 9:00am UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175/2 "2025-02-22T09:00:46Z")

</div>

I think the isbits union optimization only applies to `Array`, so not to `Memory`:

```julia-repl
julia> sizeandsummary(x) = x |> Ref .|> (sizeof, Base.summarysize)
sizeandsummary (generic function with 1 method)

julia> 1:100 |> Memory{UInt8} |> sizeandsummary
(100, 116)

julia> 1:100 |> Memory{Union{Nothing,UInt8}} |> sizeandsummary
(200, 316)

julia> 1:100 |> Vector{UInt8} |> sizeandsummary
(100, 140)

julia> 1:100 |> Vector{Union{Nothing,UInt8}} |> sizeandsummary
(100, 340)

```

---

<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 22, 2025, 10:55am UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175/3 "2025-02-22T10:55:34Z")

</div>

> [@nsajko](#):
>
> I think the isbits union optimization only applies to `Array`, so not to `Memory`:
> 
> ```julia-auto
> julia> 1:100 |> Vector{Union{Nothing,UInt8}} |> sizeandsummary
> (100, 340)
> 
> ```

But wouldn’t we expect `240` as `summarysize` if the isbits union optimization worked as documented?

I think `size` is kind of arbitrary, as it’s hard to tell what the user wants here. That being said `Memory` is closer to the truth than `Array`.

---

<div class="post-metadata">

### Author: ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)
#### Post date: [February 22, 2025, 5:47pm UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175/4 "2025-02-22T17:47:22Z")

</div>

I think this might be a bug in sumarysize.

---

<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 23, 2025, 5:12am UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175/5 "2025-02-23T05:12:01Z")

</div>

> [@](#):
>
> ```julia
> julia> 1:100 |> Memory{Union{Nothing,UInt8}} |> sizeandsummary
> (200, 316)
> 
> ```

Indeed, this seems to be the case. The [relevant code in `summarysize.jl`](https://github.com/JuliaLang/julia/blob/88aa27f482d19ae7ac0eb080c115640a0754d819/base/summarysize.jl#L152) is as follows

```julia
        dsize = sizeof(obj)
        T = eltype(obj)
        if isbitsunion(T)
            # add 1 union selector byte for each element
            dsize += length(obj)
        end

```

The line `dsize = sizeof(obj)` accounts for the union byte array (see above). As `Union{Nothing, UInt8} |> Base.isbitsunion` (unsurprisingly) returns `true`, the union byte array is accounted a second time in `dsize += length(obj)`.

I [created a ticket](https://github.com/JuliaLang/julia/issues/57506).

---

<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 23, 2025, 8:23am UTC](https://discourse.julialang.org/t/memory-isbits-union-optimization/126175/6 "2025-02-23T08:23:03Z")

</div>

I [created a PR](https://github.com/JuliaLang/julia/pull/57508). As you, @Oscar_Smith, were involved in the `sizeof` definition when `Memory` was introduced, would you be so kind to review it?
