# Help me understand constant propagation

**URL:** https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822
**Category:** General Usage
**Created:** [May 5, 2020, 6:31pm UTC](https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822 "2020-05-05T18:31:01Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)
#### Post date: [May 5, 2020, 6:31pm UTC](https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822/1 "2020-05-05T18:31:02Z")

</div>

Consider the following simple example. I have overloaded `getindex` and `getproperty` for `MyType` in the exact same way, but one allows constant propagation and the other doesn’t. I’m not even using `x[:c]` and `x.c`; I’m calling them both the same way.

```julia
using BenchmarkTools

struct Axis{IndexMap} end
Axis(;kwargs...) = Axis{(;kwargs...)}()

Base.getindex(ax::Axis, s::Symbol) = _getindex(ax, Val(s))
@generated _getindex(::Axis{IM}, ::Val{s}) where {IM,s} = :($(getfield(IM, s)))

struct MyType{T,N,A<:AbstractArray{T,N},Axes}
    data::A
    ax::Axes
end

Base.getindex(x::MyType, s::Symbol) = MyType(getfield(x, :data), getindex(getfield(x, :ax), s))
Base.getproperty(x::MyType, s::Symbol) = MyType(getfield(x, :data), getindex(getfield(x, :ax), s))

function test_index(x)
    return getindex(x, :c)
end

function test_prop(x)
    return getproperty(x, :c)
end

ax = Axis(a=1, b=2:4, c=(a=5:6, b=7))
mt = MyType(rand(7), ax)

@btime test_index($mt) # 4.600 μs (1 allocation: 16 bytes)
@btime test_prop($mt) # 5.701 ns (1 allocation: 16 bytes)

```

What’s strange is I can remove just the call to the `MyType` constructor and things are fast again:

```julia
Base.getindex(x::MyType, s::Symbol) = (getfield(x, :data), getindex(getfield(x, :ax), s))
@btime test_index($mt) # 5.701 ns (1 allocation: 16 bytes)

```

Then I thought it might help to wrap `s` as a `Val` immediately and call `_getindex` directly, but that didn’t help either:

```julia
Base.getindex(x::MyType, s::Symbol) = MyType(getfield(x, :data), _getindex(getfield(x, :ax), Val(s)))
@btime test_index($mt) # 4.628 μs (1 allocation: 16 bytes)

```

What’s going on here?

---

<div class="post-metadata">

### Author: ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)
#### Post date: [May 5, 2020, 6:37pm UTC](https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822/2 "2020-05-05T18:37:49Z")

</div>

Try with

```julia
@inline Base.getindex(x::MyType, s::Symbol) = MyType(getfield(x, :data), getindex(getfield(x, :ax), s))

```

looks like this one doesn’t inline.

---

<div class="post-metadata">

### Author: ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)
#### Post date: [May 5, 2020, 7:13pm UTC](https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822/3 "2020-05-05T19:13:21Z")

</div>

Thanks for the response. That works for this problem, but for some reason not my non-MWE. I’ll have to see what is different and possibly pull together a better example.

---

<div class="post-metadata">

### Author: ![baggepinnen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/baggepinnen/32/693_2.png) [@baggepinnen](https://discourse.julialang.org/u/baggepinnen)
#### Post date: [May 6, 2020, 6:41am UTC](https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822/4 "2020-05-06T06:41:48Z")

</div>

I have made a habit of always annotating my getindex definitions with `@inline`, are there any potential downsides to that, provided that the operation is simple?

---

<div class="post-metadata">

### Author: ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)
#### Post date: [May 6, 2020, 7:51pm UTC](https://discourse.julialang.org/t/help-me-understand-constant-propagation/38822/5 "2020-05-06T19:51:42Z")

</div>

Alright, here is a better example. This is a pared-down version of what I’m doing in [ComponentArrays.jl](https://github.com/jonniedie/ComponentArrays.jl). I’m using `@inline` for `getindex` here and am still seeing the issue.

```julia
idx_ax(x) = (x, NamedTuple())
idx_ax(x::Tuple) = x

struct Axis{IdxMap} end
Axis(IdxMap) = Axis{IdxMap}()

struct ComponentArray{Axes,T,N,A<:AbstractArray{T,N}} <: AbstractArray{T,N}
    data::A
    axes::Axes
    ComponentArray(data::A, ax::Ax) where {A<:AbstractArray{T,N},Ax<:Axis} where {T,N} = new{Ax,T,N,A}(data, ax)
    ComponentArray(data, ax) = data
end

@inline getdata(x::ComponentArray) = getfield(x, :data)
@inline getaxes(x::ComponentArray) = getfield(x, :axes)
@inline getaxes(::Type{ComponentArray{Ax,T,N,A}}) where {Ax<:Axis,T,N,A} = Ax

Base.size(x::ComponentArray) = size(getdata(x))

@inline Base.getindex(x::ComponentArray, s::Symbol) = _getindex(x, Val(s))
@inline Base.getindex(x::ComponentArray, idx) = getdata(x)[idx]
@inline Base.getindex(::Type{Axis{IdxMap}}, s::Symbol) where IdxMap = idx_ax(getfield(IdxMap, s))

@generated function _getindex(x::ComponentArray, ::Val{s}) where s
    ind_tup = getindex(getaxes(x), s)
    idx = ind_tup[1]
    new_ax = Axis(ind_tup[2])
    return :(Base.@_inline_meta; ComponentArray(Base.maybeview(getdata(x), $idx), $new_ax))
end

@inline Base.getproperty(x::ComponentArray, s::Symbol) = _getindex(x, Val(s))

```

Constant propagation works just fine on `getproperty`, but I can’t seem to get it to work with `getindex`:

```julia
ax = Axis((a=1, b=2:4, c=(5:8, (a=1:3, b=4))))
ca = ComponentArray(rand(8), ax)

using BenchmarkTools

@btime $ca.a # 1.099 ns (0 allocations: 0 bytes)
@btime $ca[:a] # 4.314 μs (1 allocation: 16 bytes)

@btime $ca.c.b # 1.099 ns (0 allocations: 0 bytes)
@btime $ca[:c][:b] # 8.933 μs (3 allocations: 80 bytes)

@btime $ca.c.a # 13.300 ns (2 allocations: 64 bytes)
@btime $ca[:c][:a] # 8.833 μs (4 allocations: 128 bytes)

function test_prop(x)
    return getproperty(x, :c)
end

function test_index(x)
    return getindex(x, :c)
end

@btime test_prop($ca) # 13.400 ns (2 allocations: 64 bytes)
@btime test_index($ca) # 4.386 μs (2 allocations: 64 bytes)

```
