# How to use getproperty efficiently?

**URL:** <https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177>\
**Category:** Performance\
**Tags:** metaprogramming\
**Created:** [January 2, 2019, 3:48am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177 "2019-01-02T03:48:50Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![anon43133060](https://avatars.discourse-cdn.com/v4/letter/a/57b2e6/32.png) [@anon43133060](https://discourse.julialang.org/u/anon43133060)\
**Post date:** [January 2, 2019, 3:48am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/1 "2019-01-02T03:48:50Z")

</div>

Inspired by [Zero-Cost Abstractions](http://www.stochasticlifestyle.com/zero-cost-abstractions-in-julia-indexing-vectors-by-name-with-labelledarrays/) I tried to implement field based access for a dictionary. It kinda works, but could it be improved? Why isn’t the last function working as fast as it should? I’m using julia v1.0.3.

```julia
using BenchmarkTools

struct A{T}
    __dict::Dict{Symbol, T}
end
a = A{Float64}(Dict(:x => 0.3))

f(a) = a.__dict[:x]
@btime f(a) # 19.032 ns (1 allocation: 16 bytes)

@inline @generated getter(a::A,::Val{s}) where s = :(getfield(a, :__dict)[s])
@inline Base.getproperty(a::A, s::Symbol) = getter(a, Val(s))
g(a) = a.x
@btime g(a) # 18.775 ns (1 allocation: 16 bytes)

@inline @generated getter(a::A,::Val{:__dict}) = :(getfield(a, :__dict))
h(a) = a.__dict
@btime h(a) # 10.469 ns (0 allocations: 0 bytes)

@inline @generated getter(a::A,::Val{s}) where s = :(a.__dict[s])
i(a) = a.x
@btime i(a) # 2.370 μs (1 allocation: 16 bytes)

```

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [January 2, 2019, 5:05am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/2 "2019-01-02T05:05:01Z")

</div>

```julia
@btime f($a) # 13.770 ns (0 allocations: 0 bytes)
@btime f(a) setup = a = A{Float64}(Dict(:x => 0.3)) # 14.301 ns (0 allocations: 0 bytes)

```

`h` just returns the `__dict` field by the way.

---

<div class="post-metadata">

**Author:** ![anon43133060](https://avatars.discourse-cdn.com/v4/letter/a/57b2e6/32.png) [@anon43133060](https://discourse.julialang.org/u/anon43133060)\
**Post date:** [January 2, 2019, 5:23am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/3 "2019-01-02T05:23:47Z")

</div>

Thanks for the tip! Still strange that replacing `a.__dict` with `h(a)` makes everything fast again:

```julia
@inline @generated getter(a::A,::Val{s}) where s = :(h(a)[s])
j(a) = a.x
@btime j($a) # 7.297 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [January 2, 2019, 5:47am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/4 "2019-01-02T05:47:53Z")

</div>

`j` and `f` benchmark the same for me; in fact their `code_llvm` is the same. Looks like the type-unstable `Val` creation is being constant-folded.

Lifting values into the type system can only serve to slow things down in this case (or give the same performance if the compiler optimizes it away, as with `j`). No amount of type system trickery can make the `Dict` lookup faster.

BTW, you need to use `Base.@_inline_meta` as the first statement in the expression returned by `@generated` functions instead of `@inline @generated`. E.g. as in

> <https://github.com/JuliaArrays/StaticArrays.jl/blob/786b6f006bd9fcbf7445e6bef018a666f88a44da/src/indexing.jl#L94>

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [January 2, 2019, 6:01am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/5 "2019-01-02T06:01:21Z")

</div>

If the properties are fixed upon construction you could use `ImmutableDict` though:

```julia
struct B{T}
    __dict::Base.ImmutableDict{Symbol, T}
end
Base.getproperty(b::B, s::Symbol) = Core.getproperty(b, :__dict)[s]

```

```julia
julia> using BenchmarkTools

julia> f(b) = b.x

julia> b = B{Float64}(Base.ImmutableDict(:x => 0.3));

julia> @btime f($b)
  0.046 ns (0 allocations: 0 bytes)
0.3

```

but:

```julia
julia> @btime f(b) setup = b = B{Float64}(Base.ImmutableDict(:x => 0.3))
  3.930 ns (0 allocations: 0 bytes)
0.3

```

In the former case, the optimizer constant-folds everything away.

Edit: but I guess at that point, just use proper fields.

---

<div class="post-metadata">

**Author:** ![bennedich](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bennedich/32/4894_2.png) [@bennedich](https://discourse.julialang.org/u/bennedich)\
**Post date:** [January 2, 2019, 6:09am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/6 "2019-01-02T06:09:25Z")

</div>

Why do you need `@generated` at all here? You’re still doing the dictionary lookup in the generated code. The thing about the blog post you linked is that it does the lookup _outside_ the generated expression, at compile time (hence “zero-cost”). Is that what you wanted to do here as well, or did you just want to implement field based dictionary access?

---

<div class="post-metadata">

**Author:** ![anon43133060](https://avatars.discourse-cdn.com/v4/letter/a/57b2e6/32.png) [@anon43133060](https://discourse.julialang.org/u/anon43133060)\
**Post date:** [January 2, 2019, 6:19am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/7 "2019-01-02T06:19:19Z")

</div>

I mainly wanted to achieve what @tkoolen has figured it out with `ImmutableDict` - so there and no dictionary lookups at all, you are right, `@generated` is not needed there.

But I also wanted to have other fields in `A` still accessible and have correct type information. Not sure if it is possible without `@generated` and `Val` trick.

---

<div class="post-metadata">

**Author:** ![anon43133060](https://avatars.discourse-cdn.com/v4/letter/a/57b2e6/32.png) [@anon43133060](https://discourse.julialang.org/u/anon43133060)\
**Post date:** [January 2, 2019, 6:22am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/8 "2019-01-02T06:22:16Z")

</div>

> [@tkoolen](#):
>
> ```julia
> Base.getproperty(b::B, s::Symbol) = Core.getproperty(b, :__dict)[s]
> 
> ```

And I was missing the `Core` vs. `Base` distinction in that line, so without `@generated` it was giving me stack overflow 🙂

---

<div class="post-metadata">

**Author:** ![tkoolen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkoolen/32/1603_2.png) [@tkoolen](https://discourse.julialang.org/u/tkoolen)\
**Post date:** [January 2, 2019, 6:49am UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/9 "2019-01-02T06:49:45Z")

</div>

> [@anon43133060](#):
>
> But I also wanted to have other fields in `A` still accessible and have correct type information. Not sure if it is possible without `@generated` and `Val` trick.

A simple `if` tree will do in this case. If the property name is a constant, the optimizer will optimize away everything but the relevant branch, making the function type stable.

```julia
struct A{T}
    __dict::Dict{Symbol, T}
end
function Base.getproperty(a::A, s::Symbol)
    if s === :__dict
        return Core.getproperty(a, :__dict)
    else
        return Core.getproperty(a, :__dict)[s]
    end
end
f(a) = a.x
g(a) = a.__dict

```

```julia
julia> a = A{Float64}(Dict(:x => 0.3));

julia> @code_warntype f(a)
Body::Float64
1 1 ─ %1 = Core.getproperty::typeof(getfield) │╻ getproperty
  │ %2 = (%1)(a, :__dict)::Dict{Symbol,Float64} ││
  │ %3 = invoke Base.ht_keyindex(%2::Dict{Symbol,Float64}, :x::Symbol)::Int64 ││╻ getindex
  │ %4 = (Base.slt_int)(%3, 0)::Bool │││╻ <
  └── goto #3 if not %4 │││
  2 ─ %6 = %new(Base.KeyError, :x)::KeyError │││╻ Type
  │ (Base.throw)(%6) │││
  └── $(Expr(:unreachable)) │││
  3 ─ %9 = (Base.getfield)(%2, :vals)::Array{Float64,1} │││╻ getproperty
  │ %10 = (Base.arrayref)(false, %9, %3)::Float64 │││╻ getindex
  └── goto #5 │││
  4 ─ $(Expr(:unreachable)) │││
  5 ┄ goto #6 ││
  6 ─ return %10 │

julia> @code_warntype g(a)
Body::Dict{Symbol,Float64}
1 1 ─ %1 = Core.getproperty::typeof(getfield) │╻ getproperty
  │ %2 = (%1)(a, :__dict)::Dict{Symbol,Float64} ││
  └── return %2

```

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [January 2, 2019, 2:34pm UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/10 "2019-01-02T14:34:09Z")

</div>

Recently I implemented something similar, I would suggest this kind of approach as @tkoolen:

> <https://github.com/chakravala/Grassmann.jl/blob/master/src/Grassmann.jl#L25>

This way you can access the fields and the dictionary. In my case, the dictionary is used to look up an index for fetching an element of an `SVector`.

---

<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:** [January 2, 2019, 4:11pm UTC](https://discourse.julialang.org/t/how-to-use-getproperty-efficiently/19177/11 "2019-01-02T16:11:15Z")

</div>

> [@chakravala](#):
>
> `Base.getproperty(a::Algebra,v::Symbol) = v ∈ [:s,:b,:g] ? getfield(a,v) : a[a.g[v]]`

This will instantiate the vector `[:s, :b, :g]` in every call and cannot be constant folded, better to use a tuple:

```julia
julia> struct Algebra
         s::Float64
         b::Int
         g::Int
       end

julia> const D = Dict(:q => 2, :t => 3)
Dict{Symbol,Int64} with 2 entries:
  :q => 2
  :t => 3

julia> Base.getproperty(a::Algebra,v::Symbol) = v ∈ [:s,:b,:g] ? getfield(a,v) : D[v]

julia> @code_warntype f(A)
Body::Union{Float64, Int64}
1 ─ %1 = invoke Base.getproperty(_2::Algebra, :s::Symbol)::Union{Float64, Int64}
└── return %1

julia> Base.getproperty(a::Algebra,v::Symbol) = v ∈ (:s,:b,:g) ? getfield(a,v) : a[a.g[v]]

julia> @code_warntype f(A)
Body::Float64
1 ─ %1 = (Main.getfield)(A, :s)::Float64
└── return %1

```
