# Discussion on Base.@pure usage

**URL:** <https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491>\
**Category:** Performance\
**Tags:** question\
**Created:** [March 4, 2021, 6:15pm UTC](https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491 "2021-03-04T18:15:00Z")\
**Posts on this page:** 4\
**Page:** 2

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [March 8, 2021, 5:41pm UTC](https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491/22 "2021-03-08T17:41:47Z")

</div>

best not to make github work too hard 🙂  
I will revise the text[s] and simply post that here.  
You can then copy the revised entry[s] into your PR.

---

<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:** [March 8, 2021, 5:43pm UTC](https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491/23 "2021-03-08T17:43:35Z")

</div>

> [@JeffreySarnoff](#):
>
> I will revise the text[s] and simply post that here.

Good for me.

I will already make a change right now because @vtjnash comments there.

---

<div class="post-metadata">

**Author:** ![rryi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rryi/32/9799_2.png) [@rryi](https://discourse.julialang.org/u/rryi)\
**Post date:** [March 9, 2021, 2:57pm UTC](https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491/24 "2021-03-09T14:57:05Z")

</div>

I am very glad that our discussion lead to a doc improvement, which is covered in PR ([39954](https://github.com/JuliaLang/julia/pull/39954)). For help on @pure usage, please continue reading that PR, or later on the official julia Base doc, once it is released.

If anyone is interested in my original question: with a slight modification, the recursive approach for \_fielddescr in V5 did the job: after wrapping the symbol by Val{s} and asserting @inbounds, compiler was able to optimize away the loop/recursion (V6). And it was surprisingly simple to solve the problem by a @generated function (V7, V8): as \_fielddescr already had only type parameters in its last version, I only had to change the return value to a (constant) tuple expression. The @generated version enforces constant propagation the hard way. Benchmarks show there is no significant speed improvement in V7/V8 on the recursive version in V6. Changes are pushed to github.

The recursive solution:

```julia
@inline function _fielddescr6(::Type{PStruct{T}},::Val{s}) where {T<:NamedTuple,s} # s isa Symbol
    _fielddescr6(Tuple{T.parameters[1]...}, T.parameters[2],Val(s),0)
end

@inline function _fielddescr6(::Type{syms}, ::Type{types},::Val{s},shift::Int) where {syms <: Tuple, types<:Tuple, s} 
    @inbounds begin
        syms===Tuple{} && throw(ArgumentError(s))
        type = Base.tuple_type_head(types)
        if s===Base.tuple_type_head(syms)
            return type, shift, bitsizeof(type)
        end
        _fielddescr6(Base.tuple_type_tail(syms),Base.tuple_type_tail(types),Val(s),shift+bitsizeof(type))
    end
end

```

The @generated solution:

```julia
@generated function __fielddescr(::Type{PStruct{T}},::Val{s}) where {T<:NamedTuple,s} # s isa Symbol
    shift = 0
    types = T.parameters[2].parameters
    syms = T.parameters[1]
    idx = 1
    while idx <= length(syms)
        type = types[idx]
        bits = bitsizeof(type)
        if syms[idx]===s
            return :(($type,$shift,$bits))
        end
        shift += bits
        idx += 1
    end
    throw(ArgumentError(s)) 
end

```

Benchmarks (V6 to V8) show that now the same runtime speed is obtained as in handcoded constant propagation (V3 and V4), all allocations are gone.

```julia
@btime bench(sv): some work on an ordinary struct, in a loop on a Vector to get stable timings
  95.776 ns (0 allocations: 0 bytes)
@btime bench(psv): same work on PStruct having same fields as struct in preceding benchmark
  823.200 μs (761 allocations: 11.89 KiB)
@btime benchV2(psv): same work, but using getpropertyV2 instead of getproperty for PStruct field access
  498.700 μs (1161 allocations: 24.39 KiB)
@btime benchV3(psv): same work, but handcoded getpropertyV3 replacing _fielddescr call by its result (simulated constant propagation)
  144.988 ns (0 allocations: 0 bytes)
@btime benchV4(psv): same work, but handcoded getpropertyV4 with resulting SHIFT and AND operation
  133.408 ns (0 allocations: 0 bytes)
@btime benchV5(psv): like V2, but recursive _fielddescr using Base.tuple_type_head and Base.tuple_type_tail in getpropertyV5
  78.100 μs (421 allocations: 6.58 KiB)
@btime benchV6(psv): like V5, but symbol wrapped in Val like in V3 and V4 and @inline assertions
  144.656 ns (0 allocations: 0 bytes)
@btime benchV7(psv): like V8, but symbol wrapped in Val
  144.934 ns (0 allocations: 0 bytes)
@btime benchV8(psv): like V2, but _fielddescr is @generated returning a constant tuple
  144.299 ns (0 allocations: 0 bytes)

```

---

<div class="post-metadata">

**Author:** ![rryi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rryi/32/9799_2.png) [@rryi](https://discourse.julialang.org/u/rryi)\
**Post date:** [March 12, 2021, 12:10am UTC](https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491/25 "2021-03-12T00:10:19Z")

</div>

benchmark figures in the solution were obtained under julia 1.6.0 RC1.

All other benchmarks were run under julia 1.5.3.

julia 1.6.0RC1 changes the picture - V6 gets as fast as V3/V4.

[Previous page](https://discourse.julialang.org/t/discussion-on-base-pure-usage/56491.md?page=1)
