-
-
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?
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -235,14 +235,14 @@ macro assert(ex, msgs...) | |
# message is an expression needing evaluating | ||
# N.B. To reduce the risk of invalidation caused by the complex callstack involved | ||
# with `string`, use `inferencebarrier` here to hide this `string` from the compiler. | ||
msg = :(Main.Base.inferencebarrier(Main.Base.string)($(esc(msg)))) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this |
||
msg = :($Main.Base.inferencebarrier($Main.Base.string)($(msg))) | ||
elseif isdefined(Main, :Base) && isdefined(Main.Base, :string) && applicable(Main.Base.string, msg) | ||
msg = Main.Base.string(msg) | ||
else | ||
# string() might not be defined during bootstrap | ||
msg = :(Main.Base.inferencebarrier(_assert_tostring)($(Expr(:quote,msg)))) | ||
msg = :($Main.Base.inferencebarrier($_assert_tostring)($(Expr(:quote,msg)))) | ||
end | ||
return :($(esc(ex)) ? $(nothing) : throw(AssertionError($msg))) | ||
return esc(:($(ex) ? $(nothing) : $Base.@outline($throw($AssertionError($msg))))) | ||
end | ||
|
||
# this may be overridden in contexts where `string(::Expr)` doesn't work | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1067,6 +1067,7 @@ export | |
@simd, | ||
@inline, | ||
@noinline, | ||
@outline, | ||
@nospecialize, | ||
@specialize, | ||
@polly, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -355,6 +355,49 @@ macro noinline(x) | |
return annotate_meta_def_or_block(x, :noinline) | ||
end | ||
|
||
""" | ||
@outline expr | ||
|
||
Outline an expression into its own function, and call that function. | ||
|
||
This macro introduces a "function barrier", which can be helpful in some code optimization | ||
scenarios. The expr is extracted into an outlined function, which is marked `@noinline`. | ||
|
||
Outlining an expr can be used to make a function smaller, e.g. by outlining an unlikely | ||
branch, which could help with runtime performance by improving instruction cache locality, | ||
and could help with compilation performance since two smaller functions can sometimes compile | ||
faster than one larger function. Finally, outlining can be useful for type-stability, by | ||
outlining a type unstable block within a hot loop, where the outlined function could be type | ||
stable. | ||
|
||
A common use case is to `@outline` the code that throws exceptions, since this should be a | ||
rare case, but it can introduce a lot of complexity to the generated code, which can | ||
sometimes harm the compiler's ability to optimize. | ||
|
||
# Examples | ||
```julia | ||
function getindex(container, index) | ||
if index < 1 || index > length(container) | ||
NHDaly marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Outline this throw, since constructing a BoundsError requires boxing | ||
# the arguments, which produces a lot of code. | ||
@outline throw(BoundsError(container, index)) | ||
end | ||
return container.data[index] | ||
end | ||
``` | ||
""" | ||
macro outline(expr) | ||
vars = esc.(_free_vars(expr)) | ||
quote | ||
@noinline outline($(vars...)) = $(esc(expr)) | ||
|
||
outline($(vars...)) | ||
end | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. I think although, what happens if you use
comparing the
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 commentThe 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 commentThe 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 commentThe 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. |
||
|
||
""" | ||
Base.@constprop setting [ex] | ||
|
||
|
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!