Skip to content
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

improve inference for getproperty #909

Merged
merged 4 commits into from
Oct 14, 2021
Merged

Conversation

simeonschaub
Copy link
Member

This has regressed quite a bit due to #848. With this PR, we should be able to get back the same performance as before, assuming there is no custom implementation or pullback for getproperty. Still need to add tests.

@willtebbutt
Copy link
Member

This is cool.

assuming there is no custom implementation or pullback for getproperty

Could you elaborate a bit on this? Why is this necessary? I'm sure it could be ascertained from the PR itself, but I could use a helping hand 😂

@DhairyaLGandhi
Copy link
Member

I am still going through the pr, it'd be good to get an idea if the generated function is too low level for Julia ir so as to break with newer releases.

@simeonschaub
Copy link
Member Author

assuming there is no custom implementation or pullback for getproperty

Could you elaborate a bit on this? Why is this necessary? I'm sure it could be ascertained from the PR itself, but I could use a helping hand 😂

Zygote's previous assumption was that getproperty could always be treated just like getfield, unless a custom adjoint was defined. This is obviously wrong if getproperty is overloaded, but it of course opens up a lot more opportunities for optimization. 😄 In the case where a user has overloaded getproperty, there is not really a way around recursing into the actual definition, but this PR special-cases the case where getproperty just calls the fallback definition, so we can basically retain the previous behavior.

@willtebbutt
Copy link
Member

Ohhh I see. This makes sense. Will comment on the PR itself.

src/lib/lib.jl Outdated
is_getfield_fallback = which(_pullback, pb_sig(x)) == which(_pullback, pb_sig(Any)) &&
which(getproperty, sig(x)) == which(getproperty, sig(Any))

if is_getfield_fallback
Copy link
Member

@willtebbutt willtebbutt Feb 24, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another high-level question, what's the reason that this entire case can't be written as

# we've ensured that we're fine to short-circuit and call `getfield` directly,
return :(Zygote._pullback(cx, literal_getfield, x, f))

rather than pulling up the IR for that method and pasting it here as is currently proposed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason is that we can't (at least that I know of) manually attach backedges to regular expressions, so we need to return a CodeInfo object. We could return a CodeInfo that just contains that call, but if we already go to lengths like this, we might as well just directly inline the whole thing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also makes sense to me.

@simeonschaub
Copy link
Member Author

I am still going through the pr, it'd be good to get an idea if the generated function is too low level for Julia ir so as to break with newer releases.

It is definitely fairly low-level, so it is not impossible this might cause problems with future Julia versions. It is basically the same mechanism IRTools and Cassette already rely upon, so while I'd definitely prefer a higher-level solution, I think it is a necessary evil if we want the inference improvements. At least in my testing, more aggressive inline annotations didn't really help.

src/lib/lib.jl Outdated Show resolved Hide resolved
src/lib/lib.jl Outdated Show resolved Hide resolved
@pevnak
Copy link

pevnak commented Mar 7, 2021

This is awesome. I have been tracking the regression. I was originally suspicious to Julia 1.6. and reported here
JuliaLang/julia#39586
which many people confirmed. Bud digging deeper, it seems to me that the problem is the getproperty

This was my MWE

using Flux
using BenchmarkTools
x = randn(Float32, 10, 10);
m = Dense(10,10);
ps = Flux.params(m);
f(w,b,x) = w * x .+ b
w, b = m.W, m.b

@btime gradient(() -> sum(m(x)), ps)
@btime gradient(() -> sum(f(w,b,x)), ps)

which on Zygote 0.5.17 with Flux 11.3 runs as

julia> @btime gradient(() -> sum(m(x)), ps)
  5.785 μs (47 allocations: 4.81 KiB)
Grads(...)

julia> @btime gradient(() -> sum(f(w,b,x)), ps)
  6.145 μs (52 allocations: 4.72 KiB)
Grads(...)

But on [email protected], Flux v0.11.6 it is fairly slow

julia> @btime gradient(() -> sum(m(x)), ps)
  25.046 μs (102 allocations: 7.39 KiB)
Grads(...)

julia> @btime gradient(() -> sum(f(w,b,x)), ps)
  6.247 μs (58 allocations: 4.91 KiB)
Grads(...)

and testing this PR with Flux v0.11.5 gives

julia> @btime gradient(() -> sum(m(x)), ps)
  5.895 μs (54 allocations: 5.02 KiB)
Grads(...)

julia> @btime gradient(() -> sum(f(w,b,x)), ps)
  6.361 μs (58 allocations: 4.91 KiB)
Grads(...)

So this is awesome. Can it be merged and tagged?

@simeonschaub
Copy link
Member Author

Yes, sorry, I had other stuff to do and didn't come around to finishing this. I will try to finish this up some time later today.

@willtebbutt
Copy link
Member

@simeonschaub how's this going? :)

@simeonschaub
Copy link
Member Author

Please don't merge this yet, it currently breaks custom adjoints for getproperty.

@simonmandlik
Copy link

simonmandlik commented May 27, 2021

Is there a chance of this being merged in the near future? :)

@simeonschaub
Copy link
Member Author

Yes, feel free to bug me about it from time to time, which probably increases the chances of me eventually finishing it. 😄 I will try to get to it soon again but every time I work on it, I discover some new issue...

@DhairyaLGandhi
Copy link
Member

bump :)

bors bot added a commit to FluxML/Flux.jl that referenced this pull request Aug 4, 2021
1681: Support NamedTuples for Chain + Parallel r=mcabbott a=mcabbott

Closes #1680, WIP. Todo list includes:

- [x] add Parallel too
- [ ] ~~worry about whether any of this will upset Zygote, like FluxML/Zygote.jl#909 or, kick that can down the road.
- [x] add tests

Co-authored-by: Michael Abbott <[email protected]>
bors bot added a commit to FluxML/Flux.jl that referenced this pull request Aug 4, 2021
1681: Support NamedTuples for Chain + Parallel r=DhairyaLGandhi a=mcabbott

Closes #1680, WIP. Todo list includes:

- [x] add Parallel too
- [ ] ~~worry about whether any of this will upset Zygote, like FluxML/Zygote.jl#909 or, kick that can down the road.
- [x] add tests

Co-authored-by: Michael Abbott <[email protected]>
bors bot added a commit to FluxML/Flux.jl that referenced this pull request Aug 4, 2021
1681: Support NamedTuples for Chain + Parallel r=darsnack a=mcabbott

Closes #1680, WIP. Todo list includes:

- [x] add Parallel too
- [ ] ~~worry about whether any of this will upset Zygote, like FluxML/Zygote.jl#909 or, kick that can down the road.
- [x] add tests

Co-authored-by: Michael Abbott <[email protected]>
@willtebbutt
Copy link
Member

@simeonschaub bump :)

This has regressed quite a bit due to FluxML#848. With this PR, we should be able to get back the same performance as before, assuming there is no custom implementation or pullback for `getproperty`. Still need to add tests.
@simeonschaub
Copy link
Member Author

Ok, sorry for taking so long to getting around for this. I have pushed some more changes and now the only broken case left is the backedges for overloading Zygote._pullback(::AContext, ::typeof(getproperty), x, ::Symbol). I am beginning to suspect this might actually be a bug in Base, but it may be enough of an edge case that we could go forward with this nevertheless.

@simeonschaub simeonschaub reopened this Oct 5, 2021
@simeonschaub simeonschaub marked this pull request as ready for review October 5, 2021 15:31
@simeonschaub
Copy link
Member Author

Any ideas why the GH actions jobs are not running?

@DhairyaLGandhi
Copy link
Member

I see they're going

Copy link
Member

@willtebbutt willtebbutt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this forward. A couple of test-related comments.

test/compiler.jl Show resolved Hide resolved
test/compiler.jl Outdated Show resolved Hide resolved
@simeonschaub simeonschaub changed the title WIP: improve inference for getproperty improve inference for getproperty Oct 12, 2021
Copy link
Member

@willtebbutt willtebbutt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fantastic work @simeonschaub . I think it just needs a patch bump, then it'll be good to go.

@willtebbutt
Copy link
Member

@simeonschaub can we bors this?

@simeonschaub
Copy link
Member Author

Sure, please go ahead!

@willtebbutt
Copy link
Member

bors r+

@bors
Copy link
Contributor

bors bot commented Oct 14, 2021

Build succeeded:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants