-
-
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
Allow constant-propagation to be disabled #42125
Conversation
Our heuristics for constant propagation are imperfect (and probably never will be perfect), and I've now seen many examples of methods that no developer would ask to have const-propped get that treatment. In some cases the cost for latency/precompilation is very large. This renames `@aggressive_constprop` to `@constprop` and allows two settings, `:aggressive` and `:none`. Closes #38983 (presuming this will propagate to kw body methods)
Actually we need to fix this as well ? julia> Base.@aggressive_constprop foo(a; kws...) = a + length(kws)
foo (generic function with 1 method)
julia> (@which foo(10)).aggressive_constprop
true
julia> (@which foo(10; kw1 = nothing)).aggressive_constprop
false The fix can be left for another PR though. |
Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
The kwfunc is missing, but the body function is covered: julia> f = Base.bodyfunction(@which foo(10; kw1 = nothing))
#foo#11 (generic function with 1 method)
julia> m = only(methods(f))
var"#foo#11"(kws::Base.Pairs{Symbol, V, Tuple{Vararg{Symbol, N}}, NamedTuple{names, T}} where {V, N, names, T<:Tuple{Vararg{Any, N}}}, ::typeof(foo), a) in Main at REPL[1]:1
julia> m.aggressive_constprop
true One consolation is that the kwfunc should be pretty quick to infer for all methods, but there's no obvious limit on the body function inference time. Still, we definitely want to fix it. |
One question: do I need to bump the serialization version number?
|
Codecov Report
@@ Coverage Diff @@
## master #42125 +/- ##
==========================================
- Coverage 79.66% 79.51% -0.15%
==========================================
Files 351 351
Lines 77567 77683 +116
==========================================
- Hits 61790 61769 -21
- Misses 15777 15914 +137
Continue to review full report at Codecov.
|
src/ircode.c
Outdated
@@ -788,7 +788,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t | |||
|
|||
jl_code_info_t *code = jl_new_code_info_uninit(); | |||
uint8_t flags = read_uint8(s.s); | |||
code->aggressive_constprop = !!(flags & (1 << 4)); | |||
code->constprop = (flags >> 4) & 3; |
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 may miss some basic understanding, but why do we need & 3
here (isn't flags >> 4
enough) ?
Another consideration here is, will we need to change this code when we add new flags (say when we add new boolean flag code->noinfer << 5
? (that's why I suggested #42125 (comment))
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 grabs both bits, without grabbing anything higher. So position 6 and higher are available and won't "contaminate" noinfer
if they are nonzero. This is essentially the same as your suggestion but briefer.
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.
One stylistic side note, instead of using the integer values directly, creating an enum and using named variables makes refactoring and understanding a tad easier.
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.
Or maybe writing it 0b11
instead of 3
, just so it's obviously "two bits"? If you created enums, I think you'd need them for both the shifts and the masks, and that would be almost a half-dozen enums for this one flag
. Do you think that really would be worth it?
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, I get it. Thanks for your explanation.
I think I will like the idea of enum (we can do that in another PR).
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.
Why not create a jl_code_info_flag_t
or something with bit fields? We use it many other places like
Lines 168 to 175 in 2f00c5f
uint16_t how:2; | |
uint16_t ndims:9; | |
uint16_t pooled:1; | |
uint16_t ptrarray:1; // representation is pointer array | |
uint16_t hasptr:1; // representation has embedded pointers | |
uint16_t isshared:1; // data is shared by multiple Arrays | |
uint16_t isaligned:1; // data allocated with memalign | |
} jl_array_flags_t; |
Do we need to keep the ABI compat for Serialization.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.
I like that idea but this worries me:
C gives no guarantee of the ordering of fields within machine words, so if you do use them for the latter reason, you program will not only be non-portable, it will be compiler-dependent too.
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.
yay, it looks much cleaner
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.
Hmm... doesn't our GC already rely on the first field of the bitfield to be the lowerest bits?
Lines 97 to 109 in 50ab3a9
struct _jl_taggedvalue_bits { | |
uintptr_t gc:2; | |
}; | |
JL_EXTENSION struct _jl_taggedvalue_t { | |
union { | |
uintptr_t header; | |
jl_taggedvalue_t *next; | |
jl_value_t *type; // 16-byte aligned | |
struct _jl_taggedvalue_bits bits; | |
}; | |
// jl_value_t value; | |
}; |
i.e.., tag->bits.gc
is used for extracting the lowest two bits of tag->type
. (But yes, it'd be nice to be standard-compliant whenever possible.)
Since v1.7 isn't released yet, I'd like to say we don't need to. Maybe @JeffBezanson has an idea on this ? |
src/ircode.c
Outdated
@@ -788,7 +788,7 @@ JL_DLLEXPORT jl_code_info_t *jl_uncompress_ir(jl_method_t *m, jl_code_instance_t | |||
|
|||
jl_code_info_t *code = jl_new_code_info_uninit(); | |||
uint8_t flags = read_uint8(s.s); | |||
code->aggressive_constprop = !!(flags & (1 << 4)); | |||
code->constprop = (flags >> 4) & 3; |
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, I get it. Thanks for your explanation.
I think I will like the idea of enum (we can do that in another PR).
I bumped the serialization number just as a precaution. If this passes I think we should merge. |
Our heuristics for constant propagation are imperfect (and probably never will be perfect), and I've now seen many examples of methods that no developer would ask to have const-propped get that treatment. In some cases the cost for latency/precompilation is very large. This renames `@aggressive_constprop` to `@constprop` and allows two settings, `:aggressive` and `:none`. Closes #38983 Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
This also adds a depwarn for `@aggressive_constprop`, which seemed sensible to me, given that it will be removed in 1.8. replaces #42125
Our heuristics for constant propagation are imperfect (and probably never will be perfect), and I've now seen many examples of methods that no developer would ask to have const-propped get that treatment. In some cases the cost for latency/precompilation is very large. This renames `@aggressive_constprop` to `@constprop` and allows two settings, `:aggressive` and `:none`. Closes JuliaLang#38983 Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
Our heuristics for constant propagation are imperfect (and probably never will be perfect), and I've now seen many examples of methods that no developer would ask to have const-propped get that treatment. In some cases the cost for latency/precompilation is very large. This renames `@aggressive_constprop` to `@constprop` and allows two settings, `:aggressive` and `:none`. Closes JuliaLang#38983 Co-authored-by: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Co-authored-by: Martin Holters <martin.holters@hsu-hh.de>
Our heuristics for constant propagation are imperfect (and probably
never will be perfect), and I've now seen many examples of
methods that no developer would ask to have const-propped get
that treatment. In some cases the cost for latency/precompilation
is very large. This renames
@aggressive_constprop
to@constprop
and allows two settings,
:aggressive
and:none
.Closes #38983 (presuming this will propagate to kw body methods, which were a huge deal in that DataFrames analysis)
Since we haven't released Julia 1.7 yet, I decided it was safe to do the rename. But if folks don't like that, we can have both
@aggressive_constprop
and@noconstprop
. JuliaHub informs me that@aggressive_constprop
is being used in half a dozen packages, but I can make PRs against them. And since it doesn't appear in our docs, it's not part of our stable interface anyway.