-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
RFC: Change lowering of destructuring to avoid const prop dependence #37268
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
Conversation
| :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
| 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 <[email protected]>
|
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
iteratewould simply return the first argument andindexwould begetfield. That way, there is no const prop,since
getfieldis called directly and inference can directlyuse its tfunc. For the fallback case
iterateis 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.