-
-
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
More aggressive allocation elimination. #16021
Conversation
The first and the second optimization combined breaks my favorite GC allocation fast path benchmark julia> function f(n)
for i in 1:n
Ref(i)
end
end
f (generic function with 1 method)
julia> @code_llvm f(10)
define void @julia_f_53825(i64) #0 {
top:
ret void
} I'm not really missing it though ;-p |
7e8290e
to
8d105b9
Compare
Any comment? |
LGTM.
You can check the used-undef flag for the slot, which is 32 (I know; will add names for these!) |
And while trying to make that change, I discovered a bug in the current implementation due to assuming linearty of control flow....... julia> function f(a, b, c)
@goto a
@label b
return j[1]
@label a
j = (a, b, c)
@goto b
end
f (generic function with 1 method)
julia> @code_warntype f(1, 1, "")
Variables:
#self#::#f
a::Int64
b::Int64
c::ASCIIString
j::Tuple{Int64,Int64,ASCIIString}
Body:
begin # REPL[1], line 2:
NewvarNode(:(j::Tuple{Int64,Int64,ASCIIString}))
goto 9 # REPL[1], line 3:
5: # REPL[1], line 4:
return (Base.getfield)(j::Tuple{Int64,Int64,ASCIIString},1)::Int64 # REPL[1], line 5:
9: # REPL[1], line 6:
GenSym(0) = a::Int64
GenSym(1) = b::Int64
GenSym(2) = c::ASCIIString # REPL[1], line 7:
goto 5
end::Int64
julia> f(1, 1, "")
signal (4): 非法指令
while loading no file, in expression starting on line 0
f at ./REPL[1]:4
unknown function (ip: 0x7f44c4111053)
[inline] at /build/julia-git/src/julia-avx2/src/julia_internal.h:69
jl_call_method_internal at /build/julia-git/src/julia-avx2/src/gf.c:1430
do_call at /build/julia-git/src/julia-avx2/src/interpreter.c:58
eval at /build/julia-git/src/julia-avx2/src/interpreter.c:181
[inline] at /build/julia-git/src/julia-avx2/src/interpreter.c:25
jl_interpret_toplevel_expr at /build/julia-git/src/julia-avx2/src/toplevel.c:535
jl_toplevel_eval_in_warn at /build/julia-git/src/julia-avx2/src/builtins.c:545
eval at ./boot.jl:236
unknown function (ip: 0x7f46c66d8258)
[inline] at /build/julia-git/src/julia-avx2/src/julia_internal.h:69
jl_call_method_internal at /build/julia-git/src/julia-avx2/src/gf.c:1430
[inline] at ./REPL.jl:3
eval_user_input at ./REPL.jl:62
unknown function (ip: 0x7f44c43b5576)
[inline] at /build/julia-git/src/julia-avx2/src/julia_internal.h:69
jl_call_method_internal at /build/julia-git/src/julia-avx2/src/gf.c:1430
[inline] at ./REPL.jl:92
#1 at ./event.jl:46
unknown function (ip: 0x7f44c459258f)
[inline] at /build/julia-git/src/julia-avx2/src/julia_internal.h:69
jl_call_method_internal at /build/julia-git/src/julia-avx2/src/gf.c:1430
[inline] at /build/julia-git/src/julia-avx2/src/julia.h:1339
jl_apply at /build/julia-git/src/julia-avx2/src/task.c:249
unknown function (ip: 0xffffffffffffffff)
Allocations: 3419201 (Pool: 3417738; Big: 1463); GC: 6 (The error is illegal instruction....) |
* Mutable allocations that aren't mutated or escaped * Allocations that aren't used (Fix #12415) * Allocations of fields that aren't used or escaped
8d105b9
to
e5d697f
Compare
The second case is handled now. julia> function f(a, b, c)
j = (a, b, c)
x, y, z = j
x + y
end
f (generic function with 1 method)
julia> @code_warntype f(1, 2, "3")
Variables:
#self#::#f
a::Int64
b::Int64
c::ASCIIString
j::Tuple{Int64,Int64,ASCIIString}
x::Int64
y::Int64
z::ASCIIString
#temp#::Int64
Body:
begin # REPL[1], line 2:
GenSym(3) = a::Int64
GenSym(4) = b::Int64
GenSym(5) = c::ASCIIString # REPL[1], line 3:
#temp#::Int64 = 1
GenSym(6) = (Base.box)(Int64,(Base.add_int)(1,1))
x::Int64 = GenSym(3)
#temp#::Int64 = GenSym(6)
GenSym(7) = (Base.box)(Int64,(Base.add_int)(2,1))
y::Int64 = GenSym(4)
#temp#::Int64 = GenSym(7)
GenSym(8) = (Base.box)(Int64,(Base.add_int)(3,1))
z::ASCIIString = GenSym(5)
#temp#::Int64 = GenSym(8) # REPL[1], line 4:
return (Base.box)(Int64,(Base.add_int)(x::Int64,y::Int64))
end::Int64 The case that used to trap should be correctly handled by type inference now too julia> function f(a, b)
@goto a
@label b
return j[1]
@label a
j = (a, b)
@goto b
end
f (generic function with 1 method)
julia> @code_warntype f(1, 2)
Variables:
#self#::#f
a::Int64
b::Int64
j::Tuple{Int64,Int64}
Body:
begin # REPL[1], line 2:
NewvarNode(:(j::Tuple{Int64,Int64}))
goto 7
4: # REPL[1], line 4:
return GenSym(0)
7: # REPL[1], line 6:
GenSym(0) = a::Int64
GenSym(1) = b::Int64 # REPL[1], line 7:
goto 4
end::Int64 However, define i64 @julia_f_53618(i64, i64) #0 {
top:
ret i64 undef
} Not sure which one is better.... |
👍 Awesome. Could you add a test for the bug you found? |
The bug isn't completely fixed yet so I didn't add the test. |
Mutable allocations that aren't mutated or escaped
I'm not sure why we were not doing this before since we have a very conservative escape check.
Maybe there's some subtlety I'm not seeing?
(It might be more useful if this can happen before type inference.)
Allocations that aren't used (Fix Omitting return value of inline function generate worse code. #12415)
This happens probably mainly due to inlining...
Allocations of fields that aren't used or escaped
The optimization still can't catch all the obvious cases. The cases I found where it still fails to optimize are mainly due to not tracing variable assignments, e.g. (note the
GenSym(1) = GenSym(0)
should be trivially optimized out since both are ssa values),Or not handling dummy use of variable in the ast (Note the
j::Tuple{Int64,Int64,ASCIIString}
at the end disables the optimization)For the second one, is there a way to check if the use can be ignored in a way that doesn't trigger #6846 ?