Skip to content

Add a statement-cost printer for analyzing inlining decisions#37275

Merged
timholy merged 1 commit intomasterfrom
teh/cost_printer
Sep 7, 2020
Merged

Add a statement-cost printer for analyzing inlining decisions#37275
timholy merged 1 commit intomasterfrom
teh/cost_printer

Conversation

@timholy
Copy link
Member

@timholy timholy commented Aug 29, 2020

While it now has a doctest, https://docs.julialang.org/en/latest/devdocs/inference/#The-inlining-algorithm-(inline_worthy)-1 has bitrotted more than once. And it's sufficiently laborious that it discourages rapid investigation. This makes printing the costs easy.

Here's a demo:

julia> Base.print_statement_costs(map, (typeof(sqrt), Tuple{Int}))
map(f, t::Tuple{Any}) in Base at tuple.jl:169
  0 1%1  = Base.getfield(_3, 1, true)::Int64
  1%2  = Base.sitofp(Float64, %1)::Float64
  2%3  = Base.lt_float(%2, 0.0)::Bool
  0 └──       goto #3 if not %3
  0 2 ─       Base.Math.throw_complex_domainerror(:sqrt, %2)::Union{}
  0 └──       unreachable
 20 3%7  = Base.Math.sqrt_llvm(%2)::Float64
  0 └──       goto #4
  0 4 ─       goto #5
  0 5%10 = Core.tuple(%7)::Tuple{Float64}
  0 └──       return %10

The numbers in the first column are the inliner's assigned cost.

If folks like this, I can add a test to make sure this stays working. (done)

Print type-inferred and optimized code for `f` given argument types `types`,
prepending each line with its cost as estimated by the compiler's inlining engine.
"""
function print_statement_costs(io::IO, @nospecialize(f), @nospecialize(t); kwargs...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it makes sense to add a @code_ version of this to InteractiveUtils. Could always be done later of course.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No way, is that @code_costs?:joy:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or an argument to one of the others (cost = true)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, I'm providing @code_costs and it's exactly @timholy who gave me the motivation to do so. 😄

I have some objections about the display style, but I agree that Julia supports that feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, nice!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like your printing quite a lot better. But if you do it as separate code-display (print(io, src); lines = readlines(io)) then one risks having the two get misaligned, whereas this version ensures that the two will always stay aligned.

Demo:

julia> function buildexpr()
           items = [7, 3]
           ex = quote
               X = $items
               for x in X
                   println(x)
               end
           end
           return ex
       end
buildexpr (generic function with 1 method)

julia> @code_typed buildexpr()
CodeInfo(
1%1  = Core.tuple(7, 3)::Core.Const((7, 3), false)
│   %2  = $(Expr(:foreigncall, :(:jl_alloc_array_1d), Vector{Int64}, svec(Any, Int64), 0, :(:ccall), Vector{Int64}, 2, 2))::Vector{Int64}
│         Base.arraysize(%2, 1)::Int64
└──       goto #7 if not true
2%5  = φ (#1 => 1, #6 => %10)::Int64%6  = φ (#1 => 1, #6 => %16)::Int64%7  = φ (#1 => 1, #6 => %17)::Int64%8  = Base.getfield(%1, %6, true)::Int64
│         Base.arrayset(false, %2, %8, %5)::Vector{Int64}%10 = Base.add_int(%5, 1)::Int64%11 = (%7 === 2)::Bool
└──       goto #4 if not %11
3 ─       goto #5
4%14 = Base.add_int(%7, 1)::Int64
└──       goto #5
5%16 = φ (#4 => %14)::Int64%17 = φ (#4 => %14)::Int64%18 = φ (#3 => true, #4 => false)::Bool%19 = Base.not_int(%18)::Bool
└──       goto #7 if not %19
6 ─       goto #2
7 ┄       goto #8
8%23 = Core._expr(:(=), :X, %2)::Expr%24 = $(Expr(:copyast, :($(QuoteNode(:(for x = X
      #= REPL[4]:6 =#
      println(x)
  end))))))::Expr%25 = Core._expr(:block, $(QuoteNode(:(#= REPL[4]:4 =#))), %23, $(QuoteNode(:(#= REPL[4]:5 =#))), %24)::Expr
└──       return %25
) => Expr

You can see statement %24 took more than one line to print.

Copy link
Contributor

@kimikage kimikage Aug 31, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just made a change that will make it easier to fix that problem. Perhaps that will be fixed in a few hours. 😃
Edit: Done. CodeCosts.jl v0.2.0 is available.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I switched the order of the printing so the cost comes first...I just needed to look a little harder at how these functions were working. I edited the top post to show the current output.

This comment was marked as resolved.

@timholy timholy removed the needs tests Unit tests are required for this change label Aug 31, 2020
@kimikage
Copy link
Contributor

kimikage commented Aug 31, 2020

  0 2 ─       Base.Math.throw_complex_domainerror(:sqrt, %2)::Union{}
  0 └──       unreachable

BTW, is this some kind of magic?

@timholy
Copy link
Member Author

timholy commented Sep 1, 2020

#35982 and #30222 (the latter should be closed once I split out a couple of nuggets into a separate PR)

@kimikage
Copy link
Contributor

kimikage commented Sep 1, 2020

What I'm suggesting is the cost "zero". 😅

Edit:
Sorry. WRT the former I was aware of the one in May (returning a flat cost, not zero), so I had not understood the relationship with the latter.

@timholy
Copy link
Member Author

timholy commented Sep 1, 2020

I'm not sure I understand your point, but the zero for the throw comes from

extyp = line == -1 ? Any : src.ssavaluetypes[line]
if extyp === Union{}
return 0

The old demo at https://docs.julialang.org/en/v1.6-dev/devdocs/inference/ set line to -1 (because the simple approach using map doesn't allow it), so it didn't return 0.

@timholy timholy merged commit a108d6c into master Sep 7, 2020
@timholy timholy deleted the teh/cost_printer branch September 7, 2020 08:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants