-
-
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
Add a new macro @outline
, and use it in @assert
.
#57122
base: master
Are you sure you want to change the base?
Conversation
Compiler/test/inline.jl
Outdated
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'm not sure if this file was the right place to put the @outline
tests. It seems decent, but a bit oxymoronic. :D Please let me know if you have any better suggestions!
Macro usage: ```julia @BoundsCheck i > 1 && i <= len || @outline throw(BoundsError(x, i)) ``` This commit applies the above to Assertions, e.g.: ```julia julia> @macroexpand @Assert x != x "x == x: $x" :(if x != x nothing else #= REPL[3]:36 =# var"#17#outline"(x) = begin $(Expr(:meta, :noinline)) #= REPL[3]:36 =# (throw)((AssertionError)(((Main).Base.inferencebarrier((Main).Base.string))("x == x: $(x)"))) end #= REPL[3]:38 =# var"#17#outline"(x) end) ``` This can improve performance for fast code that uses assertions, e.g.: Before: ```julia julia> @Btime Base.Sort.WithoutMissingVector($(Any[1]))[$1] 3.041 ns (0 allocations: 0 bytes) 1 ``` After: ```julia julia> @Btime Base.Sort.WithoutMissingVector($(Any[1]))[$1] 2.250 ns (0 allocations: 0 bytes) 1 ``` The number of instructions in that function according to `@code_native` (on an aarch64 M2 MacBook) reduced from ~90 to ~40.
Co-authored-by: adienes <[email protected]>
2fb251c
to
91876ea
Compare
end | ||
_free_vars(s::Symbol) = [s] | ||
_free_vars(_) = [] | ||
_free_vars(e::Expr) = isempty(e.args) ? [] : unique!(mapreduce(_free_vars, vcat, e.args)) |
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 think unique!
is not defined yet so this fails to compile for me
although, what happens if you use outline
as a zero-arg closure and don't pass the vars in explicitly at all? like
macro outline2(expr)
local fname = gensym(:outlined_expr)
quote
@noinline $(fname)() = $(esc(expr))
$(fname)()
end
end
comparing the @code_native
of
foo1() = @outline rand() > 0.5 || throw(AssertionError("abc"))
foo2() = @outline2 rand() > 0.5 || throw(AssertionError("abc"))
it seems a lot simpler, but I'm not an expert at these things so maybe I'm missing something
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 difference is in how they handle type instability:
julia> function bar()
x = rand(Bool) ? 7 : 7.0
@outline x*x+x*x+x*x*x+x/x-x+x
end
bar (generic function with 1 method)
julia> function bar2()
x = rand(Bool) ? 7 : 7.0
@outline2 x*x+x*x+x*x*x+x/x-x+x
end
bar2 (generic function with 1 method)
julia> @b bar
6.373 ns
julia> @b bar2
75.544 ns (2.40 allocs: 38.346 bytes)
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.
Yeah, the issue with 0-arg closures is that they can box their arguments in a bunch of caess. I was hoping to avoid that by taking advantage of macro-magic. :)
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.
Thanks for the bug report though - shouldn't be too hard to fix. I didn't notice working locally with Revise.
This implements #21925 I think. |
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.
_free_vars
extracts all vars, not just free vars. This is an issue when the outlined body defines bound variables:
julia> function bar(n)
x = rand(Bool) ? 7 : 7.0
@outline sum(x^2 for _ in 1:n)
end
ERROR: syntax: all-underscore identifiers are write-only and their values cannot be used in expressions around REPL[1]:6
Stacktrace:
[1] top-level scope
@ REPL[13]:1
julia> function bar(n)
x = rand(Bool) ? 7 : 7.0
@outline sum(x^2 for this_name_not_defined_in_main in 1:n)
end
bar (generic function with 1 method)
julia> bar(4)
ERROR: UndefVarError: `this_name_not_defined_in_main` not defined in `Main`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
[1] macro expansion
@ ./REPL[1]:6 [inlined]
[2] bar(n::Int64)
@ Main ./REPL[40]:3
[3] top-level scope
@ REPL[41]:1
Also tagging triage to discuss a significant new feature.
Co-authored-by: Lilith Orion Hafner <[email protected]>
Adds a new macro
@outline
, and uses it in@assert
.This implements the simplest suggestion in #29688.
It would still be excellent if the compiler would automatically perform this optimization for any
throw()
statement, but it sounds like introducing automatic outlining for such cases would be a difficult implementation task in the Compiler.And in either case, even if we had such an optimization, there may be cases where a human will want to perform this optimization themselves for a number of reasons, including code-size reduction, improving type stability, outlining rare branches, etc, and having this in a macro is a nice way to automate it, and make this optimization more accessible. 😊
Example macro usage:
This commit applies this new macro
@outline
to@assert
, producing essentially:or in a more complete example:
This can improve performance for fast code that uses assertions. For example, take this definition of
getindex
forWithoutMissingVector
:julia/base/sort.jl
Lines 597 to 601 in 5058dba
Before:
After:
The number of instructions in that function according to
@code_native
(on an aarch64 M2 MacBook) reduced from ~90 to ~40.