-
-
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
Fix some overly optimistic fastpaths in subtyping #25796
Conversation
184cb99
to
8f47a60
Compare
Ok, the recursive type definition requires Anyway, this now passes tests (locally, hoping CI agrees), and fixes a bug, so getting a review would be appreciated. |
src/jltypes.c
Outdated
@@ -583,6 +583,11 @@ static int is_typekey_ordered(jl_value_t **key, size_t n) | |||
!(jl_is_datatype(k) && (((jl_datatype_t*)k)->uid || | |||
(!jl_has_free_typevars(k) && !contains_unions(k))))) | |||
return 0; | |||
if (jl_is_datatype(k)) { |
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.
This probably subsumes the jl_is_datatype(k)
part of the check above (line 583).
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.
Ah, right, recursing down the structure once should be enough. Will push a simplified version once checks pass locally.
src/jltypes.c
Outdated
return 1; | ||
|
||
return is_typekey_ordered(jl_svec_data(dt1->parameters), jl_svec_len(dt1->parameters)) == | ||
is_typekey_ordered(jl_svec_data(dt2->parameters), jl_svec_len(dt2->parameters)); |
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.
This predicate seems pretty expensive. If we're going to use this, we should probably cache this property like we do for has-free-typevars.
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.
Sure. Performance-wise, I'm more worried about taking the fast-paths in subtype.c much less often, though. BTW, does nanosoldier have any benchmarks that exercise the type system?
If we're going to use this
Before I set into motion working on the caching: What are the chances of that becoming true? (And what are the alternatives?)
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've added the precomputing/caching of typekeyordered
. Any idea for a benchmark showing how helpful that is? touch base/compiler/compiler.jl; time make
and make test-subtype
are pretty much unaffected.
Where was this call coming from? Was it the call to |
I admit I got nowhere when I tried to walk through the code in the debugger and figure why it was ending in a stack overflow. So I added debug output showing all type pairs where the result of So looking at the stack traces of all calls to
BTW, there are also calls to |
Bump. Should I work on caching the result of |
Bumpity. |
9e3c147
to
9d9257d
Compare
Looking at #31696, it still fails in this branch, but only because |
I've removed Lines 223 to 225 in 9d9257d
and spent some time in the debugger to figure out why that makes struct T22624{A,B,C}; v::Vector{T22624{Int64,A}}; end overflow the stack. The call to Lines 1103 to 1104 in 9d9257d
is invoked for pi == Main.T22624{Int64, Int64, C} where C , tw == Main.T22624{A, B, C} where C where B where A . That subtype test invokes rename_unionall , which wants to construct another T22624 , resulting in the infinite recursion. I think guarding the call to jl_types_equal with a suitable predicate allows to keep #22624 fixed and at the same time fix #31696. I'll poke at it a bit more...
|
9d9257d
to
b430127
Compare
Yep, this can be fixed by adding a suitable guard, see last commit. However, I have no idea how to justify this. My guess is it's just a more or less accidental fix. Hence, the name |
Nice, thanks for figuring that out. I'll take a look. |
b430127
to
89998d7
Compare
src/jltypes.c
Outdated
return 0; | ||
jl_datatype_t *dt1 = (jl_datatype_t *) t1; | ||
jl_datatype_t *dt2 = (jl_datatype_t *) t2; | ||
if (!(is_cacheable(dt1) || jl_svec_len(dt1->parameters) == 0)) { |
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.
could be nice to have some comments here about what the reasoning here is, what the parameters are, etc.
This is trying to normalize types that might equal |
I guess calling |
See #31828 for a possible remedy concerning the normalization to wrapper/infinite recursion issue. |
FWIW, the lines of code immediately subsequent (
|
Right. But it's the comparison to the potential wrapper that is most likely to require a |
Any ideas anyone about the failed |
5aa5e82
to
7765964
Compare
Ok, rebasing made the CI error go away. And yay, all green! |
7765964
to
2a19cc8
Compare
Fixed another fastpath found in Note that this PR does not improve the normalization, but aims at avoiding bugs due to incomplete normalization. E.g. the types from #35130 are still not normalized any better, but they now compare equal as they should. While better normalization would certainly be good, I doubt it can ever be perfect, except for an uncoditional linear cache search which is probably too expensive. So I think we should apply the fixes from this PR and try to improve normalization. RA new round of review from @vtjnash / @JeffBezanson would be appreciated. |
2a19cc8
to
b13f9cd
Compare
Bump. As this fixes a bug and resolves an existential crisis, it would be nice to make some progress here. The main point I'm unsure about is the caching of |
Since this passes tests and has gotten no responses despite multiple pings over a long period, I will merge this if there's no replies in the next day or so. |
This seems slightly expensive, and still will be wrong in many other places in the system, so I'm inclined to try to go the normalization route to get the majority of cases, and see what it breaks with these rules:
|
Ref. #36211 for an experiment going the normalization route (where I'd also appreciate feedback). In general, I think to thoroughly fix the situation, an approach like in this PR will be needed. Even if there are more places around which need fixing to call
Expensive in terms of what? Do you have an idea for a benchmark to evaluate the cost of this approach? |
As it seems to be quite fashionable at the moment, I measured the TTFP: julia> @time using Plots
15.696126 seconds (22.13 M allocations: 1.186 GiB, 3.09% gc time) # before
10.652585 seconds (10.04 M allocations: 630.085 MiB, 2.28% gc time) # after
julia> @time plot(rand(10))
3.294725 seconds (3.64 M allocations: 188.339 MiB, 2.04% gc time) # before
3.257043 seconds (3.64 M allocations: 188.792 MiB, 1.37% gc time) # after Before is 8c65f8d, not current master, as I didn't rebase. Given the latest latency improvements, I probably should repeat this after a rebase, but from this benchmark, it doesn't look expensive at all. Quite on the contrary, actually. (Without precomputation of |
After rebasing (to 6185d24), |
Sill trying to get a feel for the cost of this, I can first report that I'm a bit puzzled by julia> @btime ==(Vector{Tuple{Integer}}, Vector{Tuple{>:Int}})
11.548 μs (6 allocations: 320 bytes) # master
5.343 μs (6 allocations: 320 bytes) # PR
false Why is this getting faster? Does it involve an @vtjnash, you claimed that this "seems slightly expensive". Any idea for a benchmark where might actually show? |
I think it's ok to merge this. It fixes some things, and there doesn't seem to be a cost. I do agree we should do more in the future to help ensure that equality_is_identity is true for types used as type tags ( |
b13f9cd
to
d8054fd
Compare
Fixes #25430, see discussion therein.
WIP because it leads to this regression (hence the disabled core test):
Hints welcome about where I should look to fix this!