-
-
Notifications
You must be signed in to change notification settings - Fork 213
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
Take nothing seriously #967
Conversation
I will review in next few days. |
Digging in old issues:
|
What's the intended use case for this? |
The ability to write rrule(::typeof(foo), ::AbstractArray) = <some_generic_code>
rrule(::typeof(foo), ::SubtypeOfAbstractArray) = nothing to say "don't use the generic rrule for this subtype, try and AD through it". |
src/compiler/chainrules.jl
Outdated
if m.method === chainrules_fallback | ||
# no rule exists | ||
return false, m.instance | ||
elseif Core.Compiler.return_type(rrule, Tuple{T.parameters...}) === Nothing | ||
Core.println(" has_chain_rrule got Nothing") | ||
# or we hit a rule telling us to keep digging |
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.
the chainrules_fallback
is just one rule that matches this pattern so we can get rid of the clause mentioning it explictly.
and can delete the const
on line 1 of this fule
if m.method === chainrules_fallback | |
# no rule exists | |
return false, m.instance | |
elseif Core.Compiler.return_type(rrule, Tuple{T.parameters...}) === Nothing | |
Core.println(" has_chain_rrule got Nothing") | |
# or we hit a rule telling us to keep digging | |
if Core.Compiler.return_type(rrule, Tuple{T.parameters...}) === Nothing | |
# no applicable rule |
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.
Is excluding this likely to affect compile time? IIUC @mcabbott 's reason for keeping this branch was to avoid having to do type inference some of the time.
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.
Yes, I was initially thinking not to call Compiler.return_type
more times than needed, but without thinking much about whether that actually matters. This does seem to produce specialisations, so I guess there may be some cost, even if all of them are very simple?
julia> f(x) = nothing; f(1); f(1.0); f(1+im);
julia> first(methods(f)).specializations
svec(MethodInstance for f(::Int64), MethodInstance for f(::Float64), MethodInstance for f(::Complex{Int64}), #undef, #undef, #undef, #undef, #undef)
julia> g(x) = nothing; Core.Compiler.return_type(g, Tuple{Int}); Core.Compiler.return_type(g, Tuple{Float64}); Core.Compiler.return_type(g, Tuple{String});
julia> first(methods(g)).specializations
svec(MethodInstance for g(::Int64), MethodInstance for g(::Float64), MethodInstance for g(::String), #undef, #undef, #undef, #undef, #undef)
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 am not sure that it will actually make a difference in this case though, I think we might already have had the specialization made to the the m.instance
made.
On that basis, I wonder if there is a place in IRTools
that we should be asking for the return type?
Or if we should be passing m.instance
to something we can ccall
?
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.
IRTools I don't know.
Is there some subtlety of testing the negative if m.method !== chainrules_fallback
(before this PR) which I'm overlooking?
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.
fbb3fcf makes a change like the suggestion. See what CI thinks of it. This seems to let tests pass without refresh
, which is strange but welcome.
@@ -41,7 +41,7 @@ end | |||
end | |||
|
|||
@testset "ChainRules" begin | |||
include("chainrules.jl") | |||
# include("chainrules.jl") |
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.
Don't forget to
# include("chainrules.jl") | |
include("chainrules.jl") |
Yeah, i think this is the difference.
This is a bug in Zygote right now, that needs to be tracked down
It is attaching a backedge to the method instance it hit (not the method, but the exact specialization for that input), so that if it gets invalidated this code (in theory) get invalidated too.
they are here |
Agree. Perhaps the documentation ought to stress that It also might be worth adding a friendly error, since
OK, that's good to hear. It is possible that this case was behaving a little worse than other rules, for me, but it is also possible that I got confused between all the branches & restarts & |
It is weird that we need more Anyway, running those benchmarks would be nice still |
Benchmarks from this post, #366:
(To also run the version before ChainRules, I think you need Second run, same steps after re-starting Julia, but only showing a few:
So I think the speedup for Edit -- after d015df3 this is back to the first (slower) set of times above. The second (faster) set were from 388bd0f which needed many |
Status here is that the change not to use Perhaps a little more testing by hand would not be a bad idea. I mean to check that new rules do take effect as you'd expect. |
hmmm, in #990 doesn't use the |
@vtjnash says that #990 (comment)
So I guess we can't do this |
Replaced by #1035, right? |
Right, it would have been nice to do it this way instead, but it has proved impossible. |
ChainRules has a method
rrule(_...) = nothing
to indicate that no rule has been defined, which is the signal for Zygote to keep looking inside the function. At present, this is done by checking that this exact method would be the one called, rather than allowing other user-definedrrule
methods which returnnothing
to play the same role. This fixes that, by checking the return type not just the method's identity.I'm told that compile time might be a concern. This appears to be unchanged locally.
All but 3 of the tests of ChainRules integration pass if I try them individually, but many seem to fail when running
Pkg.test
, I'm not sure why. All other tests pass.