-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Change lowering of destructuring to avoid const prop dependence #37268
Conversation
@@ -66,7 +66,7 @@ const TAGS = Any[ | |||
:(=), :(==), :(===), :gotoifnot, :A, :B, :C, :M, :N, :T, :S, :X, :Y, :a, :b, :c, :d, :e, :f, | |||
:g, :h, :i, :j, :k, :l, :m, :n, :o, :p, :q, :r, :s, :t, :u, :v, :w, :x, :y, :z, :add_int, | |||
:sub_int, :mul_int, :add_float, :sub_float, :new, :mul_float, :bitcast, :start, :done, :next, | |||
:indexed_iterate, :getfield, :meta, :eq_int, :slt_int, :sle_int, :ne_int, :push_loc, :pop_loc, | |||
:index_and_iterate, :getfield, :meta, :eq_int, :slt_int, :sle_int, :ne_int, :push_loc, :pop_loc, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be super conservative, we should leave this here and replace a reserved slot with the new symbol instead. Of course, loading code with indexed_iterate
won't work anyway, but at least it's slightly more compatible.
👍 to the concept. Shouldn't it be called |
base/missing.jl
Outdated
@@ -69,6 +69,7 @@ convert(::Type{T}, x::T) where {T>:Union{Missing, Nothing}} = x | |||
convert(::Type{T}, x) where {T>:Missing} = convert(nonmissingtype_checked(T), x) | |||
convert(::Type{T}, x) where {T>:Union{Missing, Nothing}} = convert(nonmissingtype_checked(nonnothingtype_checked(T)), x) | |||
|
|||
index_and_iterate(::Missing) = throw(MethodError(iterate, (missing,))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume this is there for the same reason as the one for nothing
, but would it make sense to add a comment here as well?
I'm currently doing some work with inference passes that have const prop (temporarily) disabled and I noticed we actually rely on it quite a bit for basic things. That's not terrible - const prop works pretty well after all, but it still imposes a cost and while I want to support it in my AD use case also, it makes destructuring quite expensive, because everything needs to be inferred twice. This PR is an experiment in changing the lowering to avoid having to const prop the index. Rather than lowering `(a,b,c) = foo()` as: ``` it = foo() a, s = indexed_iterate(it, 1) b, s = indexed_iterate(it, 2) c, s = indexed_iterate(it, 3) ``` we lower as: ``` it = foo() iterate, index = iterate_and_index(it) x = iterate(it) a = index(x, 1) y = iterate(it, y) b = index(y, 2) z = iterate(it, z) c = index(z, 3) ``` For tuples `iterate` would simply return the first argument and `index` would be `getfield`. That way, there is no const prop, since `getfield` is called directly and inference can directly use its tfunc. For the fallback case `iterate` is basically just `Base.iterate`, with just a slight tweak to give an intelligent error for short iterables. On simple functions, there isn't much of a difference in execution time, but benchmarking something more complicated like: ``` function g() a, = getfield(((1,),(2.0,3),("x",),(:x,)), Base.inferencebarrier(1)) nothing end ``` shows about a 20% improvement in end-to-end inference/optimize time, which is substantial.
So after thinking about #37268, I was wondering what would happen if one applied the same trick to the lowering of `getproperty` as well. This PR starts at that, but is mostly a placeholder for discussion of whether this is something we want to do. In particular, this PR lowers `a.b` to: ``` getproperty(a)(a, :b) ``` with a default implementation of ``` getproperty(a) = getfield ``` The inference benefit is as expected. Timing end-to-end inference&optimize time of: ``` struct Foo; end f(a::Foo) = a.x ``` shows that time to infer `f` drops by about a third (because it doesn't need to infer getproperty twice). Overall build time of the system image drops by 6% for me. If we do decide to go this way, things that would still need to be fixed: - [] Do the same for `setproperty!` - [] `ccall` doesn't like this lowering. Didn't look into it too closely - [] Needs a fallback to backwards compatibility. I'm thinking something along the lines of ``` getproperty(x) = hasmethod(getproperty, Tuple{typeof(x), Symbol}) ? getproperty : getfield ``` but obviously it would need inference integration to be fast.
Co-authored-by: Ciro Cavani <ciro.cavani@gmail.com>
Bump. Is this good to go? |
Just bumping this again. 🙂 |
We decided we may want to get rid of special lowering for destructuring, so we decided against this. |
I'm currently doing some work with inference passes that have const
prop (temporarily) disabled and I noticed we actually rely on it
quite a bit for basic things. That's not terrible - const prop works
pretty well after all, but it still imposes a cost and while I want
to support it in my AD use case also, it makes destructuring quite
expensive, because everything needs to be inferred twice. This PR
is an experiment in changing the lowering to avoid having to const
prop the index. Rather than lowering
(a,b,c) = foo()
as:we lower as:
For tuples
iterate
would simply return the first argument andindex
would begetfield
. That way, there is no const prop,since
getfield
is called directly and inference can directlyuse its tfunc. For the fallback case
iterate
is basicallyjust
Base.iterate
, with just a slight tweak to give an intelligenterror for short iterables.
On simple functions, there isn't much of a difference in execution
time, but benchmarking something more complicated like:
shows about a 20% improvement in end-to-end inference/optimize time,
which is substantial.