Recognize "interesting" call sites.
These are call sites where one of the callees accounts for a large portion of the total time of its caller. When such call sites are considered inlining failures, we can refine recommendations. The concept of interesting call site is also useful as a building block for higher-level patterns.
This commit is contained in:
parent
bb9f4e4634
commit
fa01a5304a
|
@ -172,10 +172,14 @@
|
||||||
[(log-entry kind msg stx located-stx pos provenance)
|
[(log-entry kind msg stx located-stx pos provenance)
|
||||||
|
|
||||||
;; #f if no profiling info is available for this function
|
;; #f if no profiling info is available for this function
|
||||||
|
;; takes in either a single pos number or a pair of numbers (line col)
|
||||||
(define (pos->node pos)
|
(define (pos->node pos)
|
||||||
(and profile
|
(and profile
|
||||||
(for/first ([p (in-list (profile-nodes profile))]
|
(for/first ([p (in-list (profile-nodes profile))]
|
||||||
#:when (equal? pos (node-pos p)))
|
#:when (if (pair? pos)
|
||||||
|
(and (equal? (car pos) (node-line p))
|
||||||
|
(equal? (cdr pos) (node-col p)))
|
||||||
|
(equal? pos (node-pos p))))
|
||||||
p)))
|
p)))
|
||||||
(define profile-entry (pos->node pos))
|
(define profile-entry (pos->node pos))
|
||||||
|
|
||||||
|
@ -189,6 +193,17 @@
|
||||||
|
|
||||||
;; We consider that a function is a loop if it gets inlined in itself
|
;; We consider that a function is a loop if it gets inlined in itself
|
||||||
;; at least once.
|
;; at least once.
|
||||||
|
(define is-a-loop?
|
||||||
|
(or (any-self-o-o-f? log) (> (n-unrollings log) 0)))
|
||||||
|
;; From now on, we ignore self-out-of-fuels.
|
||||||
|
(set! log (filter (lambda (l) (not (self-out-of-fuel? l))) log))
|
||||||
|
|
||||||
|
(define inlining-sites
|
||||||
|
(group-by equal? #:key (lambda (x)
|
||||||
|
(inlining-event-where-loc
|
||||||
|
(inliner-log-entry-inlining-event x)))
|
||||||
|
log))
|
||||||
|
|
||||||
;; We treat loops specially, mostly to avoid spurious reports.
|
;; We treat loops specially, mostly to avoid spurious reports.
|
||||||
;; For instance, if `f' is a loop, and gets inlined in `g' multiple
|
;; For instance, if `f' is a loop, and gets inlined in `g' multiple
|
||||||
;; times, it's likely to be unrolling. Same for out-of-fuels in `g'.
|
;; times, it's likely to be unrolling. Same for out-of-fuels in `g'.
|
||||||
|
@ -203,30 +218,48 @@
|
||||||
;; floats, in which case having all calls to `f' originate from `f''s
|
;; floats, in which case having all calls to `f' originate from `f''s
|
||||||
;; body (as opposed to `g') may make unboxing possible.
|
;; body (as opposed to `g') may make unboxing possible.
|
||||||
;; Of course, we lose precision if `g' has multiple call sites to `f'.
|
;; Of course, we lose precision if `g' has multiple call sites to `f'.
|
||||||
(define is-a-loop?
|
(set! inlining-sites
|
||||||
(or (any-self-o-o-f? log) (> (n-unrollings log) 0)))
|
(if (not is-a-loop?)
|
||||||
;; From now on, we ignore self-out-of-fuels.
|
inlining-sites
|
||||||
(set! log (filter (lambda (l) (not (self-out-of-fuel? l))) log))
|
;; `f' is a loop. We ignore anything beyond the first inlining
|
||||||
(define inlining-sites
|
;; in `g'.
|
||||||
(group-by equal? #:key (lambda (x)
|
(for/list ([site (in-list inlining-sites)])
|
||||||
(inlining-event-where-loc
|
;; If at least one inlining of `f' in `g', ignore the rest.
|
||||||
(inliner-log-entry-inlining-event x)))
|
(or (for/first ([evt (in-list site)] #:when (success? evt))
|
||||||
log))
|
(list evt))
|
||||||
|
site))))
|
||||||
|
|
||||||
(define pruned-log
|
;; Some sites are especially interesting if we have profile data.
|
||||||
(if (not is-a-loop?)
|
;; If the function under consideration takes a large portion of the
|
||||||
log
|
;; total time for a given call site, and is not inlined there, may
|
||||||
;; `f' is a loop. We ignore anything beyond the first inlining
|
;; be worth reporting.
|
||||||
;; in `g'.
|
;; returns: `(,caller-profile-node . ,call-site-log-entries) OR #f
|
||||||
(apply
|
(define interesting-sites
|
||||||
append
|
(and profile-entry
|
||||||
(for/list ([site (in-list inlining-sites)])
|
(filter values
|
||||||
;; If at least one inlining of `f' in `g', ignore the rest.
|
(for/list ([site (in-list inlining-sites)]
|
||||||
(or (for/first ([evt (in-list site)] #:when (success? evt))
|
;; Not inlined enough at that call site.
|
||||||
(list evt))
|
#:when (counts-as-a-missed-opt? site))
|
||||||
site)))))
|
(match (inlining-event-where-loc
|
||||||
(when (null? pruned-log)
|
(inliner-log-entry-inlining-event (car site)))
|
||||||
(prune))
|
[`(,caller-path ,caller-line ,caller-col)
|
||||||
|
(define caller-node
|
||||||
|
(pos->node (cons caller-line caller-col)))
|
||||||
|
(define edge
|
||||||
|
(for/first ([e (node-callers profile-entry)]
|
||||||
|
#:when (eq? (edge-caller e)
|
||||||
|
caller-node))
|
||||||
|
e))
|
||||||
|
;; Does this edge take a "large enough" proportion of
|
||||||
|
;; the caller's total time?
|
||||||
|
(and edge caller-node
|
||||||
|
(> (edge-caller-time edge)
|
||||||
|
(* (node-total caller-node) 0.5))
|
||||||
|
(cons caller-node site))]
|
||||||
|
[_ ; can't parse that, give up
|
||||||
|
#f])))))
|
||||||
|
|
||||||
|
(define pruned-log (apply append inlining-sites))
|
||||||
|
|
||||||
(define recommendation
|
(define recommendation
|
||||||
(cond [is-a-loop?
|
(cond [is-a-loop?
|
||||||
|
@ -235,18 +268,43 @@
|
||||||
;; Non-recursive function -> macro
|
;; Non-recursive function -> macro
|
||||||
"Consider turning this function into a macro to force inlining."]))
|
"Consider turning this function into a macro to force inlining."]))
|
||||||
|
|
||||||
(if (counts-as-a-missed-opt? pruned-log)
|
(cond [(and profile (not (null? interesting-sites)))
|
||||||
(missed-opt-log-entry
|
;; Inlining was not satisfactory for some call sites where we
|
||||||
kind
|
;; accounted for a good portion of the caller's total time.
|
||||||
(format "Missed Inlining ~a\n~a"
|
(missed-opt-log-entry
|
||||||
(format-aggregation-string pruned-log) recommendation)
|
kind
|
||||||
stx located-stx pos provenance
|
(format "Missed Inlining ~a\n~a\n~a"
|
||||||
'() '()
|
(format-aggregation-string pruned-log)
|
||||||
(group-badness pruned-log))
|
(format "Key call site~a: ~a"
|
||||||
(opt-log-entry
|
(if (> (length interesting-sites) 1) "s" "")
|
||||||
kind
|
(string-join
|
||||||
(format "Inlining ~a" (format-aggregation-string pruned-log))
|
(for/list ([site (in-list interesting-sites)])
|
||||||
stx located-stx pos provenance))])))
|
(define node (car site))
|
||||||
|
(format "~a ~a:~a"
|
||||||
|
(node-id node)
|
||||||
|
(node-line node)
|
||||||
|
(node-col node)))
|
||||||
|
", "))
|
||||||
|
recommendation)
|
||||||
|
stx located-stx pos provenance
|
||||||
|
'() '()
|
||||||
|
;; only compute badness for the interesting sites
|
||||||
|
(group-badness (apply append (map cdr interesting-sites))))]
|
||||||
|
[(counts-as-a-missed-opt? pruned-log)
|
||||||
|
;; Overall inlining ratio is not satisfactory.
|
||||||
|
(missed-opt-log-entry
|
||||||
|
kind
|
||||||
|
(format "Missed Inlining ~a\n~a"
|
||||||
|
(format-aggregation-string pruned-log) recommendation)
|
||||||
|
stx located-stx pos provenance
|
||||||
|
'() '()
|
||||||
|
(group-badness pruned-log))]
|
||||||
|
[else
|
||||||
|
;; Satisfactory.
|
||||||
|
(opt-log-entry
|
||||||
|
kind
|
||||||
|
(format "Inlining ~a" (format-aggregation-string pruned-log))
|
||||||
|
stx located-stx pos provenance)])])))
|
||||||
|
|
||||||
(define (group-badness group)
|
(define (group-badness group)
|
||||||
(+ (n-failures group) (- (n-out-of-fuels group) (n-successes group))))
|
(+ (n-failures group) (- (n-out-of-fuels group) (n-successes group))))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user