Add futures visualizer, improvements to futures logging
This commit is contained in:
parent
48e154e3cb
commit
b6f71ec4be
47
collects/racket/future/private/constants.rkt
Normal file
47
collects/racket/future/private/constants.rkt
Normal file
|
@ -0,0 +1,47 @@
|
|||
#lang racket/base
|
||||
(provide DEF-WINDOW-WIDTH
|
||||
DEF-WINDOW-HEIGHT
|
||||
RT-THREAD-ID
|
||||
MIN-SEG-WIDTH
|
||||
STROKE-WIDTH
|
||||
MIN-SEG-INNER-WIDTH
|
||||
DEFAULT-TIME-INTERVAL
|
||||
TIMELINE-HEADER-OPACITY
|
||||
CONNECTION-LINE-HAT-THRESHOLD
|
||||
HAT-HEIGHT
|
||||
CREATE-GRAPH-NODE-DIAMETER
|
||||
CREATE-GRAPH-PADDING
|
||||
CREATE-GRAPH-MIN-ZOOM
|
||||
CREATE-GRAPH-MAX-ZOOM
|
||||
CREATE-GRAPH-DEFAULT-ZOOM
|
||||
CREATE-GRAPH-ZOOM-FACTOR
|
||||
TIMELINE-ROW-HEIGHT
|
||||
TIMELINE-MIN-TICK-PADDING
|
||||
HEADER-PADDING
|
||||
DEFAULT-TIMELINE-WIDTH
|
||||
HEADER-HEIGHT
|
||||
TOOLTIP-MARGIN)
|
||||
|
||||
(define DEF-WINDOW-WIDTH 1500)
|
||||
(define DEF-WINDOW-HEIGHT 1000)
|
||||
(define RT-THREAD-ID 0)
|
||||
(define MIN-SEG-WIDTH 10)
|
||||
(define STROKE-WIDTH 2)
|
||||
(define MIN-SEG-INNER-WIDTH (- MIN-SEG-WIDTH STROKE-WIDTH))
|
||||
;Default time interval (in MS) between ticks on the timeline
|
||||
(define DEFAULT-TIME-INTERVAL (/ 1 10))
|
||||
(define TIMELINE-HEADER-OPACITY 0.6)
|
||||
(define CONNECTION-LINE-HAT-THRESHOLD 20)
|
||||
(define HAT-HEIGHT 9)
|
||||
(define CREATE-GRAPH-NODE-DIAMETER 30)
|
||||
(define CREATE-GRAPH-PADDING 5)
|
||||
(define CREATE-GRAPH-MIN-ZOOM 1)
|
||||
(define CREATE-GRAPH-MAX-ZOOM 5)
|
||||
(define CREATE-GRAPH-DEFAULT-ZOOM 3)
|
||||
(define CREATE-GRAPH-ZOOM-FACTOR .4)
|
||||
(define TIMELINE-ROW-HEIGHT 100)
|
||||
(define TIMELINE-MIN-TICK-PADDING 10)
|
||||
(define HEADER-PADDING 5)
|
||||
(define DEFAULT-TIMELINE-WIDTH 1000)
|
||||
(define HEADER-HEIGHT 30)
|
||||
(define TOOLTIP-MARGIN 5)
|
126
collects/racket/future/private/display.rkt
Normal file
126
collects/racket/future/private/display.rkt
Normal file
|
@ -0,0 +1,126 @@
|
|||
#lang racket/base
|
||||
(provide get-event-color
|
||||
get-event-forecolor
|
||||
header-forecolor
|
||||
header-backcolor
|
||||
timeline-event-baseline-color
|
||||
event-connection-line-color
|
||||
event-target-future-line-color
|
||||
timeline-tick-color
|
||||
timeline-tick-bold-color
|
||||
timeline-tick-label-backcolor
|
||||
timeline-tick-label-forecolor
|
||||
timeline-baseline-color
|
||||
timeline-frame-color
|
||||
timeline-frame-bg-color
|
||||
timeline-event-strokecolor
|
||||
hover-tickline-color
|
||||
create-graph-node-backcolor
|
||||
create-graph-node-strokecolor
|
||||
create-graph-node-forecolor
|
||||
create-graph-edge-color
|
||||
create-graph-block-node-forecolor
|
||||
create-graph-sync-node-forecolor
|
||||
get-time-string
|
||||
(struct-out viewable-region)
|
||||
viewable-region-x-extent
|
||||
viewable-region-y-extent
|
||||
in-viewable-region
|
||||
in-viewable-region-horiz
|
||||
scale-viewable-region
|
||||
between)
|
||||
|
||||
(struct viewable-region (x y width height) #:transparent)
|
||||
|
||||
;;viewable-region-x-extent : viewable-region -> uint
|
||||
(define (viewable-region-x-extent vregion)
|
||||
(+ (viewable-region-x vregion) (viewable-region-width vregion)))
|
||||
|
||||
;;viewable-region-y-extent : viewable-region -> uint
|
||||
(define (viewable-region-y-extent vregion)
|
||||
(+ (viewable-region-y vregion) (viewable-region-height vregion)))
|
||||
|
||||
(define (scale-viewable-region vreg factor)
|
||||
(define (scale n) (* n factor))
|
||||
(struct-copy viewable-region vreg
|
||||
[width (scale (viewable-region-width vreg))]
|
||||
[height (scale (viewable-region-height vreg))]))
|
||||
|
||||
|
||||
;;between : uint uint uint -> bool
|
||||
(define (between x start end)
|
||||
(and (>= x start) (<= x end)))
|
||||
|
||||
;;in-viewable-region : viewable-region uint -> bool
|
||||
(define (in-viewable-region-horiz vregion x)
|
||||
(between x (viewable-region-x vregion) (viewable-region-x-extent vregion)))
|
||||
|
||||
;;in-viewable-region : viewable-region segment -> bool
|
||||
(define (in-viewable-region vregion x y w h)
|
||||
(define-values (start-x start-y end-x end-y)
|
||||
(values (viewable-region-x vregion)
|
||||
(viewable-region-y vregion)
|
||||
(viewable-region-x-extent vregion)
|
||||
(viewable-region-y-extent vregion)))
|
||||
(define-values (x-end y-end)
|
||||
(values (+ x w)
|
||||
(+ y h)))
|
||||
(and (or (between x start-x end-x)
|
||||
(between x-end start-x end-x)
|
||||
(between start-x x x-end)
|
||||
(between end-y y y-end))
|
||||
(or (between y start-y end-y)
|
||||
(between y-end start-y end-y)
|
||||
(between start-y y y-end)
|
||||
(between end-y y y-end))))
|
||||
|
||||
;;get-event-color : symbol -> string
|
||||
(define (get-event-color type)
|
||||
(case type
|
||||
[(create) "blue"]
|
||||
[(start-work start-0-work touch-resume) "green"]
|
||||
[(block touch) "red"]
|
||||
[(sync) "orange"]
|
||||
[(touch-pause) "blue"]
|
||||
[(result abort suspend) "white"]
|
||||
[(complete end-work) "white"]
|
||||
[else "black"]))
|
||||
|
||||
;;get-event-forecolor : symbol -> string
|
||||
(define (get-event-forecolor type)
|
||||
(case type
|
||||
[(block) "white"]
|
||||
[else "black"]))
|
||||
|
||||
(define (header-forecolor) "white")
|
||||
(define (header-backcolor) "slategray")
|
||||
(define (timeline-event-baseline-color) "gray")
|
||||
(define (event-connection-line-color) "orchid")
|
||||
(define (event-target-future-line-color) "orange")
|
||||
(define (creation-line-color) "green")
|
||||
(define (touch-line-color) "red")
|
||||
(define (timeline-tick-color) "gray")
|
||||
(define (timeline-tick-bold-color) "darkgray")
|
||||
(define (timeline-tick-label-backcolor) "darkgray")
|
||||
(define (timeline-tick-label-forecolor) "white")
|
||||
(define (timeline-baseline-color) "darkgray")
|
||||
(define (timeline-frame-color) "gray")
|
||||
(define (timeline-frame-bg-color) "white")
|
||||
(define (timeline-event-strokecolor) "darkgray")
|
||||
(define (hover-tickline-color) "darkgray")
|
||||
(define (create-graph-node-forecolor) "white")
|
||||
(define (create-graph-node-backcolor) "steelblue")
|
||||
(define (create-graph-node-strokecolor) "darkgray")
|
||||
(define (create-graph-edge-color) "black")
|
||||
(define (create-graph-block-node-forecolor) "white")
|
||||
(define (create-graph-sync-node-forecolor) "white")
|
||||
|
||||
(define (get-time-string time)
|
||||
(if (or (= 0.0 time) (> time 0.1))
|
||||
(format "~a ms" time)
|
||||
(format "~a μs" (* 1000 time))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
109
collects/racket/future/private/drawing-helpers.rkt
Normal file
109
collects/racket/future/private/drawing-helpers.rkt
Normal file
|
@ -0,0 +1,109 @@
|
|||
#lang racket/base
|
||||
(require slideshow/pict
|
||||
"display.rkt"
|
||||
"constants.rkt")
|
||||
(provide opacity-layer
|
||||
circle-pict
|
||||
rect-pict
|
||||
text-pict
|
||||
text-block-pict
|
||||
draw-line-onto
|
||||
make-stand-out
|
||||
at
|
||||
draw-stack-onto)
|
||||
|
||||
;;opacity-layer : float uint uint -> pict
|
||||
(define (opacity-layer alpha w h)
|
||||
(cellophane (colorize (filled-rectangle w h)
|
||||
"white")
|
||||
0.6))
|
||||
|
||||
;;circle-pict : string string uint [uint] -> pict
|
||||
(define (circle-pict color stroke-color width #:stroke-width [stroke-width 1])
|
||||
(pin-over (colorize (filled-ellipse width
|
||||
width)
|
||||
stroke-color)
|
||||
(* stroke-width 2)
|
||||
(* stroke-width 2)
|
||||
(colorize (filled-ellipse (- width (* stroke-width 4))
|
||||
(- width (* stroke-width 4)))
|
||||
color)))
|
||||
|
||||
;;rect-pict : string string uint uint [uint] -> pict
|
||||
(define (rect-pict color stroke-color width height #:stroke-width [stroke-width 1])
|
||||
(pin-over (colorize (filled-rectangle width height)
|
||||
stroke-color)
|
||||
(* stroke-width 2)
|
||||
(* stroke-width 2)
|
||||
(colorize (filled-rectangle (- width (* stroke-width 4))
|
||||
(- height (* stroke-width 4)))
|
||||
color)))
|
||||
|
||||
;;text-pict : string [string] -> pict
|
||||
(define (text-pict t #:color [color "black"])
|
||||
(colorize (text t) color))
|
||||
|
||||
;;text-block-pict : string [string] [string] [uint] [float] [uint] [uint] -> pict
|
||||
(define (text-block-pict t #:backcolor [backcolor "white"]
|
||||
#:forecolor [forecolor "black"]
|
||||
#:padding [padding 10]
|
||||
#:opacity [opacity 1.0]
|
||||
#:width [width 0]
|
||||
#:height [height 0])
|
||||
(let* ([textp (colorize (text t) forecolor)]
|
||||
[padx2 (* padding 2)]
|
||||
[text-cont (pin-over (blank (+ (pict-width textp) padx2)
|
||||
(+ (pict-height textp) padx2))
|
||||
padding
|
||||
padding
|
||||
textp)]
|
||||
[bg (cellophane (colorize (filled-rectangle (max width (pict-width text-cont))
|
||||
(max height (pict-height text-cont)))
|
||||
backcolor)
|
||||
opacity)])
|
||||
(lc-superimpose bg text-cont)))
|
||||
|
||||
;;draw-line-onto : pict uint uint uint uint string -> pict
|
||||
(define (draw-line-onto base
|
||||
startx
|
||||
starty
|
||||
endx
|
||||
endy
|
||||
color
|
||||
#:width [width 1]
|
||||
#:with-arrow [with-arrow #f]
|
||||
#:arrow-sz [arrow-sz 10]
|
||||
#:style [style 'solid])
|
||||
(let ([dx (- endx startx)]
|
||||
[dy (- endy starty)]
|
||||
[line-f (if with-arrow pip-arrow-line pip-line)])
|
||||
(pin-over base
|
||||
startx
|
||||
starty
|
||||
(linewidth width
|
||||
(linestyle style
|
||||
(colorize (line-f dx
|
||||
dy
|
||||
arrow-sz)
|
||||
color))))))
|
||||
|
||||
;;make-stand-out : pict -> pict
|
||||
(define (make-stand-out pict)
|
||||
(scale pict 2))
|
||||
|
||||
(struct draw-at (x y p) #:transparent)
|
||||
|
||||
;;at : uint uint pict -> draw-at
|
||||
(define (at x y p)
|
||||
(draw-at x y p))
|
||||
|
||||
;;draw-stack-onto : pict (listof pict) -> pict
|
||||
(define (draw-stack-onto base . picts)
|
||||
(for/fold ([p base]) ([cur-p (in-list picts)])
|
||||
(cond
|
||||
[(pict? cur-p) (pin-over p 0 0 cur-p)]
|
||||
[(draw-at? cur-p) (pin-over p
|
||||
(draw-at-x cur-p)
|
||||
(draw-at-y cur-p)
|
||||
(draw-at-p cur-p))]
|
||||
[else (error 'draw-onto "Invalid argument in 'picts' list.")])))
|
440
collects/racket/future/private/graph-drawing.rkt
Normal file
440
collects/racket/future/private/graph-drawing.rkt
Normal file
|
@ -0,0 +1,440 @@
|
|||
#lang racket
|
||||
(require rackunit)
|
||||
(provide (struct-out point)
|
||||
(struct-out node)
|
||||
(struct-out drawable-node)
|
||||
(struct-out graph-layout)
|
||||
draw-tree
|
||||
drawable-node-center)
|
||||
|
||||
(define DEFAULT-WIDTH 10)
|
||||
(define PADDING 5)
|
||||
(define-struct/contract point ([x integer?] [y integer?]) #:transparent)
|
||||
(struct node (data children))
|
||||
(struct graph-layout (width height nodes) #:transparent)
|
||||
(struct drawable-node (node x y width depth children children-xextent children-yextent) #:transparent)
|
||||
|
||||
(define (int x)
|
||||
(floor (exact->inexact x)))
|
||||
|
||||
;;Gets the center point of a node circle.
|
||||
;;drawable-node-center : node -> point
|
||||
(define (drawable-node-center dnode)
|
||||
(point (int (+ (drawable-node-x dnode) (/ (drawable-node-width dnode) 2)))
|
||||
(int (+ (drawable-node-y dnode) (/ (drawable-node-width dnode) 2)))))
|
||||
|
||||
;
|
||||
; ;; ;;
|
||||
; ;;; ; ; ; ;
|
||||
; ; ;; ; ; ;
|
||||
; ; ; ;;;;;;; ;;;; ;; ;;; ;;; ; ;;;; ;; ;; ;;; ;
|
||||
; ; ; ; ; ;; ; ; ;; ; ; ;;; ; ;;
|
||||
; ;;;; ; ; ; ; ; ; ; ; ; ;
|
||||
; ; ; ;;;;;; ; ; ; ; ;;;;;; ; ; ;
|
||||
; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
||||
; ; ; ; ; ; ; ; ; ; ; ; ; ; ;
|
||||
; ;; ; ; ; ;; ; ; ; ;; ; ;; ; ; ;;
|
||||
; ; ;;; ;;;; ;;;; ;; ;;; ;;; ;;; ;; ;;;; ;; ;;;;;; ;;; ;;
|
||||
;
|
||||
|
||||
;;draw-tree/standard : node uint uint uint uint uint -> drawable-node
|
||||
(define (draw-tree/standard parent x y depth node-width padding)
|
||||
(if (empty? (node-children parent))
|
||||
(drawable-node parent
|
||||
(+ padding x)
|
||||
(+ padding y)
|
||||
node-width
|
||||
depth
|
||||
'()
|
||||
(+ padding x node-width)
|
||||
(+ padding y node-width))
|
||||
(let ([child-y (+ y node-width)]
|
||||
[children (node-children parent)]
|
||||
[parenty (+ y padding)])
|
||||
(if (= 1 (length children)) ;Align parent and child vertically
|
||||
(let ([child (draw-tree/standard (first children)
|
||||
x
|
||||
(+ parenty node-width)
|
||||
(add1 depth)
|
||||
node-width
|
||||
padding)])
|
||||
(drawable-node parent
|
||||
(drawable-node-x child)
|
||||
parenty
|
||||
node-width
|
||||
depth
|
||||
(list child)
|
||||
(drawable-node-children-xextent child)
|
||||
(drawable-node-children-yextent child)))
|
||||
(let-values ([(x-extent
|
||||
y-extent
|
||||
children)
|
||||
(for/fold ([xacc x] [yacc y] [chn '()])
|
||||
([child (in-list children)])
|
||||
(let ([dchild (draw-tree/standard child
|
||||
xacc
|
||||
(+ parenty node-width)
|
||||
(add1 depth)
|
||||
node-width
|
||||
padding)])
|
||||
(values (drawable-node-children-xextent dchild)
|
||||
(drawable-node-children-yextent dchild)
|
||||
(cons dchild chn))))])
|
||||
(let* ([chn (reverse children)]
|
||||
[xmin (drawable-node-x (first chn))]
|
||||
[xmax (drawable-node-x (last chn))])
|
||||
(drawable-node parent
|
||||
(+ xmin (/ (- xmax xmin) 2))
|
||||
parenty
|
||||
node-width
|
||||
depth
|
||||
chn
|
||||
x-extent
|
||||
(+ y-extent node-width))))))))
|
||||
|
||||
;; ; ;;;
|
||||
; ;;;;;; ; ;
|
||||
; ; ;; ; ;
|
||||
; ; ; ;;;; ;;; ; ;;; ;;;; ;
|
||||
; ; ; ; ; ; ;; ; ; ; ;
|
||||
; ; ; ; ; ; ; ; ;
|
||||
; ;;;;; ;;;;;; ; ; ; ;;;;;; ;
|
||||
; ; ; ; ; ; ; ; ; ; ;
|
||||
; ; ; ; ; ; ; ; ; ; ;
|
||||
; ; ; ; ;; ; ;; ; ; ;; ;
|
||||
; ;;; ;; ;;;; ;; ;;; ;; ;;;;;; ;;;; ;; ;;;;;;
|
||||
;
|
||||
|
||||
;(r * cos(deg), r * sin(deg)) = point on circle given angle and radius r.
|
||||
|
||||
(struct attributed-node (node type num-leaves depth children))
|
||||
(define (leaf? anode)
|
||||
(equal? (attributed-node-type anode) 'leaf))
|
||||
|
||||
;;build-attr-tree : node uint -> attributed-node
|
||||
(define (build-attr-tree parent depth)
|
||||
(if (empty? (node-children parent))
|
||||
(attributed-node parent 'leaf 0 depth '())
|
||||
(let-values ([(leaves achn)
|
||||
(for/fold ([l 0] [achildren '()]) ([child (in-list (node-children parent))])
|
||||
(let ([anode (build-attr-tree child (add1 depth))])
|
||||
(if (leaf? anode)
|
||||
(values (add1 l) (cons anode achildren))
|
||||
(values (+ l (attributed-node-num-leaves anode)) (cons anode achildren)))))])
|
||||
(attributed-node parent
|
||||
'interior
|
||||
leaves
|
||||
depth
|
||||
achn))))
|
||||
|
||||
|
||||
;(struct drawable-node (node x y width depth children children-xextent children-yextent) #:transparent)
|
||||
;;draw-tree/radial : node uint (uint -> uint) uint -> drawable-node
|
||||
(define (draw-tree/radial root node-width Bv p depth)
|
||||
(let* ([atree (build-attr-tree root 0)]
|
||||
#;[angle-incr (/ Bv (length (attributed-node-children root)))])
|
||||
(for/fold ([angle 0] [chn '()]) ([achild (in-list (attributed-node-children atree))])
|
||||
(let* ([Bu (/ (* (attributed-node-num-leaves achild) Bv)
|
||||
(attributed-node-num-leaves atree))]
|
||||
[pa (+ angle
|
||||
(/ Bu 2))]
|
||||
[x (* (p depth) (cos pa))]
|
||||
[y (* (p depth) (sin pa))])
|
||||
(values (+ angle Bu)
|
||||
(cons (drawable-node (attributed-node-node achild)
|
||||
x
|
||||
y
|
||||
node-width
|
||||
depth
|
||||
'()
|
||||
0
|
||||
0) chn))))))
|
||||
|
||||
;;tree-layout/private : drawable-node uint uint (listof drawable-node) -> (values uint uint (listof drawable-node))
|
||||
(define (tree-layout/private parent xextent yextent nodes)
|
||||
(if (empty? (drawable-node-children parent))
|
||||
(values (max (+ (drawable-node-x parent) (drawable-node-width parent)) xextent)
|
||||
(max (+ (drawable-node-y parent) (drawable-node-width parent)) yextent)
|
||||
(cons parent nodes))
|
||||
(for/fold ([x xextent] [y yextent] [ns (cons parent nodes)]) ([child (in-list (drawable-node-children parent))])
|
||||
(tree-layout/private child x y (cons child ns)))))
|
||||
|
||||
;;calc-tree-layout : drawable-node uint uint -> graph-layout
|
||||
(define (calc-tree-layout root node-width padding)
|
||||
(define-values (w h nodes) (tree-layout/private root 0 0 '()))
|
||||
(graph-layout w
|
||||
h
|
||||
nodes))
|
||||
|
||||
;;draw-tree : node [symbol] [uint] [uint] [uint] -> tree-layout
|
||||
(define (draw-tree root
|
||||
#:style [style 'standard]
|
||||
#:node-width [node-width DEFAULT-WIDTH]
|
||||
#:padding [padding PADDING]
|
||||
#:zoom [zoom-level 1])
|
||||
(let* ([scaled-node-w (* node-width zoom-level)]
|
||||
[scaled-padding (* padding zoom-level)]
|
||||
[layout
|
||||
(case style
|
||||
[(standard) (calc-tree-layout (draw-tree/standard root
|
||||
0
|
||||
0
|
||||
0
|
||||
scaled-node-w
|
||||
scaled-padding)
|
||||
scaled-node-w
|
||||
scaled-padding)]
|
||||
[(radial) (calc-tree-layout (draw-tree/radial root
|
||||
(λ (i) (* i 50)))
|
||||
scaled-node-w
|
||||
scaled-padding)]
|
||||
[(hv) 0]
|
||||
[else
|
||||
(error 'draw-tree "Invalid tree drawing style.")])])
|
||||
(graph-layout (+ (graph-layout-width layout) scaled-padding)
|
||||
(+ (graph-layout-height layout) scaled-padding)
|
||||
(graph-layout-nodes layout))))
|
||||
|
||||
|
||||
;Tests
|
||||
(let* ([nodea (drawable-node (node 'a '()) 5 5 10 0 0 '() 10)]
|
||||
[center (drawable-node-center nodea)])
|
||||
(check-equal? (point-x center) 10.0)
|
||||
(check-equal? (point-y center) 10.0))
|
||||
|
||||
|
||||
(define test-padding 5)
|
||||
(define test-width 10)
|
||||
|
||||
(define (tree root-data . children)
|
||||
(node root-data children))
|
||||
|
||||
(define (get-node data layout)
|
||||
(first (filter (λ (dn) (equal? (node-data (drawable-node-node dn)) data)) (graph-layout-nodes layout))))
|
||||
|
||||
#|
|
||||
a
|
||||
|
|
||||
b
|
||||
|#
|
||||
(define tree0 (tree 'a (tree 'b)))
|
||||
(let* ([layout (draw-tree tree0 #:node-width test-width #:padding test-padding)]
|
||||
[dnode-a (get-node 'a layout)]
|
||||
[dnode-b (get-node 'b layout)])
|
||||
(check-equal? (graph-layout-width layout) (+ (* test-padding 2) test-width))
|
||||
(check-equal? (graph-layout-height layout) (+ (* test-padding 3) (* test-width 2)))
|
||||
(check-equal? (drawable-node-x dnode-a) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-a) test-padding)
|
||||
(check-equal? (drawable-node-x dnode-b) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-b) (+ test-padding test-width test-padding)))
|
||||
(let ([atree (build-attr-tree tree0 0)])
|
||||
(check-equal? (attributed-node-num-leaves atree) 1))
|
||||
|
||||
#|
|
||||
a
|
||||
/ \
|
||||
b c
|
||||
|#
|
||||
(define tree1 (tree 'a
|
||||
(tree 'b)
|
||||
(tree 'c)))
|
||||
(define layout (draw-tree tree1 #:node-width test-width #:padding test-padding))
|
||||
(for ([dnode (in-list (graph-layout-nodes layout))])
|
||||
(check-equal? (drawable-node-width dnode) test-width))
|
||||
(define dnode-a (get-node 'a layout))
|
||||
(define dnode-b (get-node 'b layout))
|
||||
(define dnode-c (get-node 'c layout))
|
||||
|
||||
(define slot-one-pos (+ test-padding test-width test-padding))
|
||||
(define square-sz (+ (* test-padding 3) (* test-width 2)))
|
||||
(check-equal? (graph-layout-width layout) square-sz)
|
||||
(check-equal? (graph-layout-height layout) square-sz)
|
||||
(check-equal? (drawable-node-x dnode-b) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-b) slot-one-pos)
|
||||
(check-equal? (drawable-node-x dnode-c) slot-one-pos)
|
||||
(check-equal? (drawable-node-y dnode-c) slot-one-pos)
|
||||
(check-equal? (drawable-node-x dnode-a) (/ 25 2))
|
||||
(check-equal? (drawable-node-y dnode-a) test-padding)
|
||||
(check-equal? (length (drawable-node-children dnode-a)) 2)
|
||||
(let ([atree (build-attr-tree tree1 0)])
|
||||
(check-equal? (attributed-node-num-leaves atree) 2))
|
||||
|
||||
#|
|
||||
a
|
||||
/ \
|
||||
b d
|
||||
| / \
|
||||
c e f
|
||||
|
|
||||
g
|
||||
|#
|
||||
(define tree2 (tree 'a
|
||||
(tree 'b
|
||||
(tree 'c))
|
||||
(tree 'd
|
||||
(tree 'e)
|
||||
(tree 'f
|
||||
(tree 'g)))))
|
||||
(let* ([layout (draw-tree tree2 #:node-width test-width #:padding test-padding)]
|
||||
[nodes (graph-layout-nodes layout)]
|
||||
[dnode-a (get-node 'a layout)]
|
||||
[dnode-b (get-node 'b layout)]
|
||||
[dnode-c (get-node 'c layout)]
|
||||
[dnode-d (get-node 'd layout)]
|
||||
[dnode-e (get-node 'e layout)]
|
||||
[dnode-f (get-node 'f layout)]
|
||||
[dnode-g (get-node 'g layout)])
|
||||
(check-equal? (node-data (drawable-node-node dnode-a)) 'a)
|
||||
(check-equal? (node-data (drawable-node-node dnode-b)) 'b)
|
||||
(check-equal? (node-data (drawable-node-node dnode-c)) 'c)
|
||||
(check-equal? (node-data (drawable-node-node dnode-d)) 'd)
|
||||
(check-equal? (node-data (drawable-node-node dnode-e)) 'e)
|
||||
(check-equal? (node-data (drawable-node-node dnode-f)) 'f)
|
||||
(check-equal? (node-data (drawable-node-node dnode-g)) 'g)
|
||||
(check-equal? (graph-layout-width layout) 50)
|
||||
(check-equal? (graph-layout-height layout) 65)
|
||||
(check-equal? (drawable-node-x dnode-a) (/ 65 4))
|
||||
(check-equal? (drawable-node-y dnode-a) test-padding)
|
||||
(check-equal? (drawable-node-x dnode-b) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-b) (+ (* 2 test-padding) test-width))
|
||||
(check-equal? (drawable-node-x dnode-c) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-c) (+ (drawable-node-y dnode-b) test-width test-padding))
|
||||
(check-equal? (drawable-node-x dnode-e) (+ (* 2 test-padding) test-width))
|
||||
(check-equal? (drawable-node-y dnode-e) (+ (drawable-node-y dnode-d) test-width test-padding))
|
||||
(check-equal? (drawable-node-x dnode-f) (+ (drawable-node-x dnode-e) test-width test-padding))
|
||||
(check-equal? (drawable-node-y dnode-f) (drawable-node-y dnode-e))
|
||||
(check-equal? (drawable-node-x dnode-g) (drawable-node-x dnode-f))
|
||||
(check-equal? (drawable-node-y dnode-g) (+ (drawable-node-y dnode-f) test-width test-padding)))
|
||||
(let ([atree (build-attr-tree tree2 0)])
|
||||
(check-equal? (attributed-node-num-leaves atree) 3))
|
||||
|
||||
#|
|
||||
a
|
||||
/|\
|
||||
b c e
|
||||
|
|
||||
d
|
||||
|#
|
||||
(define tree3 (tree 'a
|
||||
(tree 'b)
|
||||
(tree 'c
|
||||
(tree 'd))
|
||||
(tree 'e)))
|
||||
(let* ([layout (draw-tree tree3 #:node-width test-width #:padding test-padding)]
|
||||
[nodes (graph-layout-nodes layout)]
|
||||
[dnode-a (get-node 'a layout)]
|
||||
[dnode-b (get-node 'b layout)]
|
||||
[dnode-c (get-node 'c layout)]
|
||||
[dnode-d (get-node 'd layout)]
|
||||
[dnode-e (get-node 'e layout)])
|
||||
(check-equal? (graph-layout-width layout) 50)
|
||||
(check-equal? (graph-layout-height layout) 50)
|
||||
(check-equal? (drawable-node-x dnode-a) 20)
|
||||
(check-equal? (drawable-node-y dnode-a) 5)
|
||||
(check-equal? (drawable-node-x dnode-b) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-b) (+ (* 2 test-padding) test-width))
|
||||
(check-equal? (drawable-node-x dnode-c) (+ (* 2 test-padding) test-width))
|
||||
(check-equal? (drawable-node-y dnode-c) (drawable-node-y dnode-b))
|
||||
(check-equal? (drawable-node-x dnode-e) (+ (* 3 test-padding) (* 2 test-width)))
|
||||
(check-equal? (drawable-node-y dnode-e) (drawable-node-y dnode-c))
|
||||
(check-equal? (drawable-node-x dnode-d) (drawable-node-x dnode-c))
|
||||
(check-equal? (drawable-node-y dnode-d) (+ (drawable-node-y dnode-c) test-padding test-width)))
|
||||
(let ([atree (build-attr-tree tree3 0)])
|
||||
(check-equal? (attributed-node-num-leaves atree) 3))
|
||||
|
||||
#|
|
||||
a
|
||||
/ | | \
|
||||
b c f g
|
||||
/ \
|
||||
d e
|
||||
|#
|
||||
(define tree4 (tree 'a
|
||||
(tree 'b)
|
||||
(tree 'c
|
||||
(tree 'd)
|
||||
(tree 'e))
|
||||
(tree 'f)
|
||||
(tree 'g)))
|
||||
(let* ([layout (draw-tree tree4 #:node-width test-width #:padding test-padding)]
|
||||
[nodes (graph-layout-nodes layout)]
|
||||
[dnode-a (get-node 'a layout)]
|
||||
[dnode-b (get-node 'b layout)]
|
||||
[dnode-c (get-node 'c layout)]
|
||||
[dnode-d (get-node 'd layout)]
|
||||
[dnode-e (get-node 'e layout)]
|
||||
[dnode-f (get-node 'f layout)]
|
||||
[dnode-g (get-node 'g layout)])
|
||||
(check-equal? (graph-layout-width layout) 80)
|
||||
(check-equal? (graph-layout-height layout) 50)
|
||||
(check-equal? (drawable-node-x dnode-b) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-b) (+ (drawable-node-y dnode-a) test-width test-padding))
|
||||
(check-equal? (drawable-node-y dnode-c) (drawable-node-y dnode-b))
|
||||
(check-equal? (drawable-node-x dnode-d) (+ (drawable-node-x dnode-b) test-width test-padding))
|
||||
(check-equal? (drawable-node-y dnode-d) (+ (drawable-node-y dnode-c) test-width test-padding))
|
||||
(check-equal? (drawable-node-x dnode-e) (+ (drawable-node-x dnode-d) test-width test-padding))
|
||||
(check-equal? (drawable-node-y dnode-e) (drawable-node-y dnode-d))
|
||||
(check-equal? (drawable-node-x dnode-f) (+ (drawable-node-x dnode-e) test-width test-padding))
|
||||
(check-equal? (drawable-node-y dnode-f) (drawable-node-y dnode-c))
|
||||
(check-equal? (drawable-node-x dnode-g) (+ (drawable-node-x dnode-f) test-width test-padding)))
|
||||
(let ([atree (build-attr-tree tree4 0)])
|
||||
(check-equal? (attributed-node-num-leaves atree) 5))
|
||||
|
||||
#|
|
||||
Layered-tree-draw example from Di Battista
|
||||
a
|
||||
/ \
|
||||
b g
|
||||
| / \
|
||||
c h k
|
||||
| / \
|
||||
d i j
|
||||
/ \
|
||||
e f
|
||||
|#
|
||||
(define tree5 (tree 'a
|
||||
(tree 'b
|
||||
(tree 'c
|
||||
(tree 'd
|
||||
(tree 'e)
|
||||
(tree 'f))))
|
||||
(tree 'g
|
||||
(tree 'h
|
||||
(tree 'i)
|
||||
(tree 'j))
|
||||
(tree 'k))))
|
||||
(let* ([layout (draw-tree tree5 #:node-width test-width #:padding test-padding)]
|
||||
[nodes (graph-layout-nodes layout)]
|
||||
[dnode-a (get-node 'a layout)]
|
||||
[dnode-b (get-node 'b layout)]
|
||||
[dnode-c (get-node 'c layout)]
|
||||
[dnode-d (get-node 'd layout)]
|
||||
[dnode-e (get-node 'e layout)]
|
||||
[dnode-f (get-node 'f layout)]
|
||||
[dnode-g (get-node 'g layout)]
|
||||
[dnode-h (get-node 'h layout)]
|
||||
[dnode-i (get-node 'i layout)]
|
||||
[dnode-j (get-node 'j layout)]
|
||||
[dnode-k (get-node 'k layout)])
|
||||
(check-equal? (graph-layout-width layout) 80)
|
||||
(check-equal? (graph-layout-height layout) 80)
|
||||
(check-equal? (drawable-node-x dnode-e) test-padding)
|
||||
(check-equal? (drawable-node-y dnode-e) 65)
|
||||
(check-equal? (drawable-node-x dnode-f) (+ (drawable-node-x dnode-e) test-width test-padding))
|
||||
(check-equal? (drawable-node-x dnode-i) (+ (drawable-node-x dnode-f) test-width test-padding))
|
||||
(check-equal? (drawable-node-x dnode-j) (+ (drawable-node-x dnode-i) test-width test-padding))
|
||||
(check-equal? (drawable-node-x dnode-k) (+ (drawable-node-x dnode-j) test-width test-padding)))
|
||||
(let ([atree (build-attr-tree tree5 0)])
|
||||
(check-equal? (attributed-node-num-leaves atree) 5))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
213
collects/racket/future/private/gui-helpers.rkt
Normal file
213
collects/racket/future/private/gui-helpers.rkt
Normal file
|
@ -0,0 +1,213 @@
|
|||
#lang racket/gui
|
||||
(require framework
|
||||
slideshow/pict
|
||||
"display.rkt"
|
||||
"constants.rkt")
|
||||
(provide pict-canvas%
|
||||
label
|
||||
mt-label
|
||||
bold-label
|
||||
mt-bold-label
|
||||
section-header
|
||||
(struct-out event-target)
|
||||
make-listener-table
|
||||
add-receiver
|
||||
post-event)
|
||||
|
||||
(define pict-canvas%
|
||||
(class canvas%
|
||||
(init redraw-on-resize pict-builder hover-handler click-handler overlay-builder)
|
||||
(inherit get-dc get-client-size refresh get-view-start)
|
||||
(define bp pict-builder) ;Builds the main pict for the canvas
|
||||
(define mh hover-handler) ;Mouse hover handler
|
||||
(define ob overlay-builder) ;Hover overlay pict builder
|
||||
(define ch click-handler) ;Mouse click handler
|
||||
(define draw-on-resize redraw-on-resize)
|
||||
(define do-logging #f)
|
||||
(define redraw-overlay #f) ;Whether we should redraw the overlay pict in the canvas
|
||||
(define redo-bitmap-on-paint #f)
|
||||
|
||||
(define/public (set-redo-bitmap-on-paint! v)
|
||||
(set! redo-bitmap-on-paint v))
|
||||
|
||||
(define/public (set-do-logging! v)
|
||||
(set! do-logging v))
|
||||
|
||||
;;set-build-pict! : (viewable-region -> pict) -> void
|
||||
(define/public (set-build-pict! f)
|
||||
(set! bp f))
|
||||
|
||||
;;set-mouse-handler! : (uint uint -> segment) -> void
|
||||
(define/public (set-mouse-handler! f)
|
||||
(set! mh f))
|
||||
|
||||
;;set-overlay-builder! : (viewable-region -> pict) -> void
|
||||
(define/public (set-overlay-builder! f)
|
||||
(set! ob f))
|
||||
|
||||
;;set-click-handler! : (uint uint -> segment) -> void
|
||||
(define/public (set-click-handler! f)
|
||||
(set! ch f))
|
||||
|
||||
;;set-redraw-overlay! : bool -> void
|
||||
(define/public (set-redraw-overlay! b)
|
||||
(set! redraw-overlay b))
|
||||
|
||||
(define the-drawer #f)
|
||||
(define img-width 0)
|
||||
(define bm #f)
|
||||
(define overlay-pict #f)
|
||||
|
||||
(define/private (get-viewable-region)
|
||||
(define-values (x y) (get-view-start))
|
||||
(define-values (w h) (get-client-size))
|
||||
(viewable-region x y w h))
|
||||
|
||||
(define/private (overlay-drawer dc vregion)
|
||||
(when ob
|
||||
(define p (ob vregion))
|
||||
(unless (or (not p) (void? p))
|
||||
(draw-pict p
|
||||
dc
|
||||
(viewable-region-x vregion)
|
||||
(viewable-region-y vregion)))))
|
||||
|
||||
(define/private (redo-bitmap vregion)
|
||||
(when bp
|
||||
(define p (bp vregion))
|
||||
(set! bm (pict->bitmap p))))
|
||||
|
||||
(define/public (redraw-everything)
|
||||
(redo-bitmap (get-viewable-region))
|
||||
(refresh))
|
||||
|
||||
(define/override (on-size width height)
|
||||
(when (or draw-on-resize
|
||||
(not bm))
|
||||
(set! bm #f)
|
||||
(refresh))
|
||||
(set! redraw-overlay #t))
|
||||
|
||||
(define/override (on-paint)
|
||||
(define vregion (get-viewable-region))
|
||||
(when (or redo-bitmap-on-paint (not bm))
|
||||
(redo-bitmap vregion))
|
||||
(when bm
|
||||
(let ([dc (get-dc)])
|
||||
(send dc draw-bitmap
|
||||
bm
|
||||
(viewable-region-x vregion)
|
||||
(viewable-region-y vregion))
|
||||
(overlay-drawer dc vregion))))
|
||||
|
||||
(define/override (on-event event)
|
||||
(define vregion (get-viewable-region))
|
||||
(define x (+ (viewable-region-x vregion) (send event get-x)))
|
||||
(define y (+ (viewable-region-y vregion) (send event get-y)))
|
||||
(case (send event get-event-type)
|
||||
[(motion)
|
||||
(when mh (mh x y vregion))]
|
||||
[(left-up)
|
||||
(when ch (ch x y vregion))])
|
||||
(when redraw-overlay
|
||||
(refresh)))
|
||||
|
||||
(super-new)
|
||||
(send (get-dc) set-smoothing 'aligned)))
|
||||
|
||||
(define bold-system-font
|
||||
(send the-font-list find-or-create-font
|
||||
(send normal-control-font get-point-size)
|
||||
(send normal-control-font get-family)
|
||||
(send normal-control-font get-style)
|
||||
'bold))
|
||||
|
||||
(define (label p str)
|
||||
(new message% [parent p]
|
||||
[label str]
|
||||
[stretchable-width #t]))
|
||||
|
||||
(define (mt-label p)
|
||||
(label p ""))
|
||||
|
||||
(define (bold-label p str)
|
||||
(new message% [parent p]
|
||||
[label str]
|
||||
[font bold-system-font]
|
||||
[stretchable-width #t]))
|
||||
|
||||
(define (mt-bold-label p)
|
||||
(bold-label p ""))
|
||||
|
||||
(define (section-header par name orientation)
|
||||
(let* ([text-pict (colorize (text name) (header-forecolor))]
|
||||
[text-container (pin-over (colorize (rectangle (+ 10 (pict-width text-pict))
|
||||
(+ 10 (pict-height text-pict)))
|
||||
(header-backcolor))
|
||||
5
|
||||
5
|
||||
text-pict)]
|
||||
[c (case orientation
|
||||
[(horizontal)
|
||||
(let ([canv (new pict-canvas%
|
||||
[parent par]
|
||||
[redraw-on-resize #t]
|
||||
[pict-builder (λ (vregion)
|
||||
(lc-superimpose (colorize (filled-rectangle (viewable-region-width vregion)
|
||||
HEADER-HEIGHT)
|
||||
(header-backcolor))
|
||||
text-container))]
|
||||
[hover-handler #f]
|
||||
[click-handler #f]
|
||||
[overlay-builder #f]
|
||||
[min-height HEADER-HEIGHT]
|
||||
[stretchable-width #t]
|
||||
[stretchable-height #f])])
|
||||
canv)]
|
||||
[(vertical)
|
||||
(let ([canv (new pict-canvas%
|
||||
[parent par]
|
||||
[redraw-on-resize #t]
|
||||
[pict-builder (λ (vregion)
|
||||
(rotate (lc-superimpose (colorize (filled-rectangle (viewable-region-height vregion)
|
||||
HEADER-HEIGHT)
|
||||
(header-backcolor))
|
||||
text-container)
|
||||
-1.57079633))]
|
||||
[hover-handler #f]
|
||||
[click-handler #f]
|
||||
[overlay-builder #f]
|
||||
[min-width HEADER-HEIGHT]
|
||||
[stretchable-width #f]
|
||||
[stretchable-height #t])])
|
||||
canv)])])
|
||||
c))
|
||||
|
||||
;Events
|
||||
;receiver : any
|
||||
;handler : (any -> void)
|
||||
(struct event-target (receiver handler) #:transparent)
|
||||
|
||||
(define (make-listener-table) (make-hash))
|
||||
|
||||
(define (add-receiver table evt-name object handler)
|
||||
(hash-update! table
|
||||
evt-name
|
||||
(λ (old)
|
||||
(cons (event-target object handler) old))
|
||||
(list (event-target object handler))))
|
||||
|
||||
(define (post-event table name sender arg)
|
||||
(let ([targets (hash-ref table name)])
|
||||
(for ([target (in-list targets)])
|
||||
(let ([receiver (event-target-receiver target)]
|
||||
[handler (event-target-handler target)])
|
||||
(unless (eq? receiver sender)
|
||||
(handler arg))))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
365
collects/racket/future/private/visualizer-data.rkt
Normal file
365
collects/racket/future/private/visualizer-data.rkt
Normal file
|
@ -0,0 +1,365 @@
|
|||
#lang racket/base
|
||||
(require racket/bool
|
||||
racket/list
|
||||
racket/contract
|
||||
racket/future
|
||||
racket/set
|
||||
"constants.rkt"
|
||||
"graph-drawing.rkt")
|
||||
|
||||
(provide (contract-out [start-performance-tracking! (-> void?)])
|
||||
(struct-out future-event)
|
||||
(struct-out indexed-fevent)
|
||||
(struct-out trace)
|
||||
(struct-out process-timeline)
|
||||
(struct-out future-timeline)
|
||||
(struct-out event)
|
||||
(struct-out rtcall-info)
|
||||
raw-log-output
|
||||
organize-output
|
||||
build-trace
|
||||
event-has-duration?
|
||||
final-event?
|
||||
relative-time)
|
||||
|
||||
;Log message receiver
|
||||
(define recv #f)
|
||||
|
||||
;;start-performance-tracking! -> void
|
||||
(define (start-performance-tracking!)
|
||||
(when (not recv)
|
||||
(set! recv (make-log-receiver (current-logger) 'debug))))
|
||||
|
||||
(define-struct future-event (future-id process-id what time prim-name user-data)
|
||||
#:prefab)
|
||||
|
||||
;Contains an index and a future-event,
|
||||
;so we can preserve the order in which future-events
|
||||
;were logged.
|
||||
;Many future-events can be logged at what appears to be the same
|
||||
;time, apparently because the time values don't have great enough precision
|
||||
;to separate events which temporally occur close together.
|
||||
(struct indexed-fevent (index fevent) #:transparent)
|
||||
|
||||
;The whole trace, with a start/end time and list of process timelines
|
||||
(struct trace (start-time
|
||||
end-time
|
||||
proc-timelines
|
||||
future-timelines
|
||||
all-events
|
||||
real-time ;TODO: What is this
|
||||
num-futures ;TODO: (length future-timelines)
|
||||
num-blocks
|
||||
num-syncs
|
||||
blocked-futures
|
||||
avg-syncs-per-future
|
||||
block-counts ;prim name --o--> number of blocks
|
||||
sync-counts ;op name --o--> number of syncs
|
||||
future-rtcalls ;fid --o--> rtcall-info
|
||||
creation-tree))
|
||||
|
||||
(struct rtcall-info (fid
|
||||
block-hash ; prim name --o--> number of blocks
|
||||
sync-hash) ; op name --o--> number of syncs
|
||||
#:transparent)
|
||||
|
||||
;The timeline of events for a specific process
|
||||
(struct timeline (id
|
||||
start
|
||||
end
|
||||
events))
|
||||
;(struct process-timeline timeline (proc-index))
|
||||
(struct process-timeline (proc-id
|
||||
proc-index ;Why do we need this
|
||||
start-time
|
||||
end-time
|
||||
events))
|
||||
;(struct future-timeline timeline ())
|
||||
(struct future-timeline (future-id
|
||||
start-time
|
||||
end-time
|
||||
events))
|
||||
|
||||
;A block of time (e.g. a process executing a portion of a future thunk).
|
||||
(struct event (index
|
||||
start-time
|
||||
end-time
|
||||
proc-id
|
||||
proc-index ;TODO: why here?
|
||||
future-id
|
||||
user-data
|
||||
type
|
||||
prim-name
|
||||
timeline-position ;TODO: what is this
|
||||
[prev-proc-event #:mutable]
|
||||
[next-proc-event #:mutable]
|
||||
[prev-future-event #:mutable]
|
||||
[next-future-event #:mutable]
|
||||
[next-targ-future-event #:mutable]
|
||||
[prev-targ-future-event #:mutable]
|
||||
[segment #:mutable]) #:transparent)
|
||||
|
||||
;;event-has-duration? : event -> bool
|
||||
(define (event-has-duration? evt)
|
||||
(case (event-type evt)
|
||||
[(start-work start-0-work) #t]
|
||||
[else #f]))
|
||||
|
||||
;;final-event? : event -> bool
|
||||
(define (final-event? evt)
|
||||
(case (event-timeline-position evt)
|
||||
[(end singleton) #t]
|
||||
[else #f]))
|
||||
|
||||
(define (get-log-events)
|
||||
(let ([info (sync/timeout 0 recv)])
|
||||
(if info
|
||||
(let ([v (vector-ref info 2)])
|
||||
(cons v (get-log-events)))
|
||||
'())))
|
||||
|
||||
;;get-relative-start-time : trace float -> float
|
||||
(define (relative-time trace abs-time)
|
||||
(- abs-time (trace-start-time trace)))
|
||||
|
||||
;Gets log output as a straight list, ordered according to when the
|
||||
;message was logged
|
||||
;;raw-log-output : uint -> (listof indexed-fevent)
|
||||
(define (raw-log-output index)
|
||||
(let ([info (sync/timeout 0 recv)])
|
||||
(if info
|
||||
(let ([v (vector-ref info 2)])
|
||||
(if (future-event? v)
|
||||
(cons (indexed-fevent index v) (raw-log-output (add1 index)))
|
||||
(raw-log-output index)))
|
||||
'())))
|
||||
|
||||
(define (print-blocks raw-output)
|
||||
(for ([fe (in-list raw-output)])
|
||||
(when (equal? (future-event-what fe) 'block)
|
||||
(printf "~a\n" (future-event-prim-name fe)))))
|
||||
|
||||
;Produces a vector of vectors, where each inner vector contains
|
||||
;all the log output messages for a specific process
|
||||
;;organize-output : (listof indexed-fevent) -> (vectorof (vectorof future-event))
|
||||
(define (organize-output raw-log-output)
|
||||
;TODO: Try using for/set here, does calling code depend on ordering
|
||||
#;(define unique-proc-ids (for/set ([ie (in-list raw-log-output)])
|
||||
(future-event-process-id (indexed-fevent-fevent ie))))
|
||||
(let ([unique-proc-ids (sort (for/fold ([ids '()]) ([ie (in-list raw-log-output)])
|
||||
(let* ([evt (indexed-fevent-fevent ie)]
|
||||
[procid (future-event-process-id evt)])
|
||||
(if (member procid ids)
|
||||
ids
|
||||
(cons procid ids))))
|
||||
<)])
|
||||
(for/vector ([procid (in-list unique-proc-ids)])
|
||||
(for/vector ([e (in-list raw-log-output)]
|
||||
#:when (eq? procid (future-event-process-id (indexed-fevent-fevent e))))
|
||||
e))))
|
||||
|
||||
;;build-trace : (listof indexed-fevent) -> trace
|
||||
(define (build-trace log-output)
|
||||
(define data (organize-output log-output))
|
||||
(define-values (start-time end-time unique-fids nblocks nsyncs)
|
||||
(for/fold ([start-time #f]
|
||||
[end-time #f]
|
||||
[unique-fids (set)]
|
||||
[nblocks 0]
|
||||
[nsyncs 0]) ([ie (in-list log-output)])
|
||||
(let* ([evt (indexed-fevent-fevent ie)]
|
||||
[fid (future-event-future-id evt)]
|
||||
[is-future-thread? (not (= (future-event-process-id evt) RT-THREAD-ID))])
|
||||
(values
|
||||
(if start-time
|
||||
(min start-time (future-event-time evt))
|
||||
(future-event-time evt))
|
||||
(if end-time
|
||||
(max end-time (future-event-time evt))
|
||||
(future-event-time evt))
|
||||
(if fid
|
||||
(set-add unique-fids fid)
|
||||
unique-fids)
|
||||
(if (and is-future-thread?
|
||||
(case (future-event-what evt)
|
||||
[(block touch) #t]
|
||||
[else #f]))
|
||||
(add1 nblocks)
|
||||
nblocks)
|
||||
(if (and is-future-thread? (symbol=? (future-event-what evt) 'sync))
|
||||
(add1 nsyncs)
|
||||
nsyncs)))))
|
||||
(define tls (for/list ([proc-log-vec (in-vector data)]
|
||||
[i (in-naturals)])
|
||||
(let* ([fst-ie (vector-ref proc-log-vec 0)]
|
||||
[fst-log-msg (indexed-fevent-fevent fst-ie)])
|
||||
(process-timeline (future-event-process-id fst-log-msg)
|
||||
i
|
||||
(future-event-time fst-log-msg)
|
||||
(future-event-time (indexed-fevent-fevent
|
||||
(vector-ref proc-log-vec
|
||||
(sub1 (vector-length proc-log-vec)))))
|
||||
(for/list ([ie (in-vector proc-log-vec)]
|
||||
[j (in-naturals)])
|
||||
(let* ([evt (indexed-fevent-fevent ie)]
|
||||
[start (future-event-time evt)]
|
||||
[pos (cond
|
||||
[(zero? j) (if (= j (sub1 (vector-length proc-log-vec)))
|
||||
'singleton
|
||||
'start)]
|
||||
[(= j (sub1 (vector-length proc-log-vec))) 'end]
|
||||
[else 'interior])])
|
||||
(event (indexed-fevent-index ie)
|
||||
start
|
||||
(if (or (equal? pos 'end) (equal? pos 'singleton))
|
||||
start
|
||||
(future-event-time (indexed-fevent-fevent
|
||||
(vector-ref proc-log-vec (add1 j)))))
|
||||
(future-event-process-id evt)
|
||||
i
|
||||
(future-event-future-id evt)
|
||||
(future-event-user-data evt)
|
||||
(future-event-what evt)
|
||||
(future-event-prim-name evt)
|
||||
pos
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f)))))))
|
||||
(define all-evts (sort (flatten (for/list ([tl (in-list tls)])
|
||||
(process-timeline-events tl)))
|
||||
(λ (a b)
|
||||
(< (event-index a) (event-index b)))))
|
||||
(define ftls (let ([h (make-hash)])
|
||||
(for ([evt (in-list all-evts)])
|
||||
(let* ([fid (event-future-id evt)]
|
||||
[existing (hash-ref h fid '())])
|
||||
(hash-set! h fid (cons evt existing))))
|
||||
h))
|
||||
(for ([fid (in-list (hash-keys ftls))])
|
||||
(hash-set! ftls fid (reverse (hash-ref ftls fid))))
|
||||
(define-values (block-hash sync-hash rt-hash) (build-rtcall-hash all-evts))
|
||||
(define tr (trace start-time
|
||||
end-time
|
||||
tls
|
||||
ftls
|
||||
all-evts
|
||||
(- end-time start-time) ;real time
|
||||
(set-count unique-fids) ;num-futures
|
||||
nblocks ;num-blocks
|
||||
nsyncs ;num-syncs
|
||||
0
|
||||
0
|
||||
block-hash
|
||||
sync-hash
|
||||
rt-hash ;hash of fid -> rtcall-info
|
||||
(build-creation-graph ftls)))
|
||||
(connect-event-chains! tr)
|
||||
(connect-target-fid-events! tr)
|
||||
tr)
|
||||
|
||||
;;build-rtcall-hash : (listof event) -> (values (blocking_prim --o--> count) (sync_prim --o--> count) (fid --o--> rtcall-info)
|
||||
(define (build-rtcall-hash evts)
|
||||
(define block-hash (make-hash))
|
||||
(define sync-hash (make-hash))
|
||||
(define rt-hash (make-hash))
|
||||
(for ([evt (in-list (filter (λ (e) (and (= (event-proc-id e) RT-THREAD-ID)
|
||||
(or (equal? (event-type e) 'block)
|
||||
(equal? (event-type e) 'sync))))
|
||||
evts))])
|
||||
;(printf "event: ~a\n" evt)
|
||||
(define isblock (case (event-type evt)
|
||||
[(block) #t]
|
||||
[else #f]))
|
||||
(define ophash (if isblock block-hash sync-hash))
|
||||
(hash-update! ophash
|
||||
(event-prim-name evt)
|
||||
(λ (old) (add1 old))
|
||||
(λ () 1))
|
||||
(hash-update! rt-hash
|
||||
(event-future-id evt)
|
||||
(λ (old)
|
||||
(let ([h (if isblock
|
||||
(rtcall-info-block-hash old)
|
||||
(rtcall-info-sync-hash old))])
|
||||
(hash-update! h
|
||||
(event-prim-name evt)
|
||||
(λ (o) (add1 o))
|
||||
(λ () 1)))
|
||||
old)
|
||||
(λ ()
|
||||
(let* ([ri (rtcall-info (event-future-id evt) (make-hash) (make-hash))]
|
||||
[h (if isblock
|
||||
(rtcall-info-block-hash ri)
|
||||
(rtcall-info-sync-hash ri))])
|
||||
(hash-update! h
|
||||
(event-prim-name evt)
|
||||
(λ (o) (add1 o))
|
||||
(λ () 1))
|
||||
ri))))
|
||||
; (printf "blocks: ~a\n syncs: ~a\n rts: ~a\n" block-hash sync-hash rt-hash)
|
||||
(values block-hash sync-hash rt-hash))
|
||||
|
||||
|
||||
|
||||
;;connect-event-chains! : trace -> void
|
||||
(define (connect-event-chains! trace)
|
||||
(for ([tl (in-list (trace-proc-timelines trace))])
|
||||
(let loop ([evts (process-timeline-events tl)])
|
||||
(if (or (empty? evts) (empty? (cdr evts)))
|
||||
void
|
||||
(begin
|
||||
(set-event-prev-proc-event! (first (cdr evts)) (car evts))
|
||||
(set-event-next-proc-event! (car evts) (first (cdr evts)))
|
||||
(loop (cdr evts))))))
|
||||
(for ([fid (in-list (hash-keys (trace-future-timelines trace)))])
|
||||
(let ([events (hash-ref (trace-future-timelines trace) fid)])
|
||||
(let loop ([evts events])
|
||||
(if (or (empty? evts) (empty? (cdr evts)))
|
||||
void
|
||||
(begin
|
||||
(set-event-prev-future-event! (first (cdr evts)) (car evts))
|
||||
(set-event-next-future-event! (car evts) (first (cdr evts)))
|
||||
(loop (cdr evts))))))))
|
||||
|
||||
;;connect-target-fid-events! : trace -> void
|
||||
(define (connect-target-fid-events! trace)
|
||||
(let loop ([rest (trace-all-events trace)])
|
||||
(unless (empty? rest)
|
||||
(let ([cur-evt (car rest)])
|
||||
(when (and (or (equal? (event-type cur-evt) 'create)
|
||||
(equal? (event-type cur-evt) 'touch))
|
||||
(not (zero? (event-user-data cur-evt))))
|
||||
(let ([targ-evt (findf (λ (e) (and (event-future-id e)
|
||||
(= (event-future-id e)
|
||||
(event-user-data cur-evt))))
|
||||
(cdr rest))])
|
||||
(when targ-evt
|
||||
(set-event-next-targ-future-event! cur-evt targ-evt)
|
||||
(set-event-prev-targ-future-event! targ-evt cur-evt))))
|
||||
(loop (cdr rest))))))
|
||||
|
||||
;;creation-event : event -> bool
|
||||
(define (creation-event? evt)
|
||||
(equal? (event-type evt) 'create))
|
||||
|
||||
;;buid-creation-graph/private : (uint -o-> (listof future-event)) -> (listof node)
|
||||
(define (build-creation-graph/private future-timelines evt)
|
||||
(let* ([fid (event-user-data evt)]
|
||||
[fevents (filter creation-event? (hash-ref future-timelines fid))])
|
||||
(for/list ([cevt (in-list fevents)])
|
||||
(node cevt
|
||||
(build-creation-graph/private future-timelines cevt)))))
|
||||
|
||||
;;build-creation-graph : (uint -o-> (listof future-event)) -> node
|
||||
(define (build-creation-graph future-timelines)
|
||||
(define roots (filter creation-event?
|
||||
(hash-ref future-timelines #f)))
|
||||
(define root-nodes (for/list ([root (in-list roots)])
|
||||
(node root
|
||||
(build-creation-graph/private future-timelines root))))
|
||||
(node 'runtime-thread
|
||||
root-nodes))
|
831
collects/racket/future/private/visualizer-drawing.rkt
Normal file
831
collects/racket/future/private/visualizer-drawing.rkt
Normal file
|
@ -0,0 +1,831 @@
|
|||
#lang racket/base
|
||||
(require racket/list
|
||||
racket/class
|
||||
racket/draw
|
||||
slideshow/pict
|
||||
data/interval-map
|
||||
"visualizer-data.rkt"
|
||||
"graph-drawing.rkt"
|
||||
"drawing-helpers.rkt"
|
||||
"display.rkt"
|
||||
"constants.rkt")
|
||||
|
||||
(provide seg-in-vregion
|
||||
calc-segments
|
||||
calc-ticks
|
||||
calc-row-mid-y
|
||||
find-seg-for-coords
|
||||
segment-edge
|
||||
segs-equal-or-later
|
||||
build-timeline-pict
|
||||
build-timeline-bmp-from-log
|
||||
build-timeline-pict-from-log
|
||||
build-timeline-overlay
|
||||
build-timeline-with-overlay
|
||||
build-timeline-bmp-with-overlay
|
||||
build-creategraph-pict
|
||||
graph-overlay-pict
|
||||
(struct-out segment)
|
||||
(struct-out frame-info)
|
||||
(struct-out timeline-tick)
|
||||
find-node-for-coords
|
||||
find-fid-for-coords
|
||||
first-seg-for-fid)
|
||||
|
||||
;Represents a dot or square on the timeline
|
||||
(struct segment (event
|
||||
x
|
||||
y
|
||||
width
|
||||
height
|
||||
color
|
||||
p
|
||||
prev-future-seg
|
||||
next-future-seg
|
||||
prev-proc-seg
|
||||
next-proc-seg
|
||||
prev-targ-future-seg
|
||||
next-targ-future-seg) #:transparent #:mutable)
|
||||
|
||||
|
||||
;General information about the timeline image
|
||||
(struct frame-info (adjusted-width
|
||||
adjusted-height
|
||||
row-height
|
||||
modifier
|
||||
timeline-ticks
|
||||
process-line-coords) #:transparent)
|
||||
|
||||
;Represents a vertical line depicting a specific time in the execution history
|
||||
(struct timeline-tick (x
|
||||
abs-time
|
||||
rel-time) #:transparent)
|
||||
|
||||
;;viewable-region-from-frame : frame-info -> viewable-region
|
||||
(define (viewable-region-from-frame finfo)
|
||||
(viewable-region 0
|
||||
0
|
||||
(frame-info-adjusted-width finfo)
|
||||
(frame-info-adjusted-height finfo)))
|
||||
|
||||
;;seg-in-vregion : viewable-region segment -> bool
|
||||
(define (seg-in-vregion vregion)
|
||||
(λ (seg)
|
||||
(in-viewable-region vregion
|
||||
(segment-x seg)
|
||||
(segment-y seg)
|
||||
(segment-width seg)
|
||||
(segment-height seg))))
|
||||
|
||||
;;calc-seg-x : event process-timeline trace uint float -> uint
|
||||
(define (calc-seg-x evt tr modifier)
|
||||
(floor (* (relative-time tr (event-start-time evt))
|
||||
modifier)))
|
||||
|
||||
;;calc-seg-width : float event -> uint
|
||||
(define (calc-seg-width modifier evt)
|
||||
(case (event-type evt)
|
||||
[(start-work start-0-work) (max MIN-SEG-WIDTH (* modifier (- (event-end-time evt)
|
||||
(event-start-time evt))))]
|
||||
[else MIN-SEG-WIDTH]))
|
||||
|
||||
;Finds the segment for given x and y mouse coordinates
|
||||
;;find-seg-for-coords : uint uint interval-map -> segment
|
||||
(define (find-seg-for-coords x y index)
|
||||
(let ([xmap (interval-map-ref index y #f)])
|
||||
(if xmap
|
||||
(interval-map-ref xmap x #f)
|
||||
#f)))
|
||||
|
||||
;;find-fid-for-coords : uint uint (listof drawable-node) -> drawable-node
|
||||
(define (find-node-for-coords x y nodes)
|
||||
(define node-l (filter (λ (n)
|
||||
(define n-x (drawable-node-x n))
|
||||
(define n-y (drawable-node-y n))
|
||||
(define n-w (drawable-node-width n))
|
||||
(and (n-x . < . x)
|
||||
(n-y . < . y)
|
||||
(x . < . (+ n-x n-w))
|
||||
(y . < . (+ n-y n-w))))
|
||||
(remove-duplicates (flatten nodes))))
|
||||
(cond
|
||||
[(empty? node-l)
|
||||
#f]
|
||||
[(= 1 (length node-l))
|
||||
(car node-l)]
|
||||
[else
|
||||
(error 'find-node-for-coords "Multiple nodes found for coords: ~s ~s, ~s" x y node-l)]))
|
||||
|
||||
;;find-fid-for-coords : x y ??(listof (listof nodes)) by depth?? viewable-region -> fid
|
||||
(define (find-fid-for-coords x y nodes vregion)
|
||||
(define n (find-node-for-coords x y nodes))
|
||||
(if n
|
||||
(event-user-data (node-data (drawable-node-node n)))
|
||||
#f))
|
||||
|
||||
;;first-seg-for-fid : future-id (listof segments) -> segment
|
||||
(define (first-seg-for-fid fid segs)
|
||||
(first
|
||||
(sort
|
||||
(filter (λ (s) (define seg-fid (event-future-id (segment-event s)))
|
||||
(and seg-fid fid (= fid seg-fid))) segs)
|
||||
< #:key (λ (s) (event-start-time (segment-event s))))))
|
||||
|
||||
;;calc-adjusted-width : uint trace -> uint
|
||||
(define (calc-adjusted-width w tr)
|
||||
(define baseModifier (/ w (- (trace-end-time tr) (trace-start-time tr))))
|
||||
(define max-x-extent (for*/fold ([x 0]) ([tl (in-list (trace-proc-timelines tr))]
|
||||
[evt (in-list (process-timeline-events tl))])
|
||||
(max (+ (calc-seg-x evt tr baseModifier)
|
||||
(calc-seg-width baseModifier evt))
|
||||
x)))
|
||||
(- (- w (- max-x-extent w)) MIN-SEG-WIDTH))
|
||||
|
||||
;;calc-row-mid-y : uint uint -> uint
|
||||
(define (calc-row-mid-y proc-index row-height)
|
||||
(floor (- (+ (* proc-index
|
||||
row-height)
|
||||
(/ row-height 2))
|
||||
2)))
|
||||
|
||||
;Gets the center of a circle with (xleft, ytop) as the top-left coordinate.
|
||||
;;calc-center : uint uint uint -> (values uint uint)
|
||||
(define (calc-center xleft ytop diameter)
|
||||
(let ([rad (floor (/ diameter 2))])
|
||||
(values (+ xleft rad)
|
||||
(+ ytop rad))))
|
||||
|
||||
;;segs-equal-or-after : float (listof segment) -> (listof segment)
|
||||
(define (segs-equal-or-later real-time segs)
|
||||
(let loop ([sgs segs])
|
||||
(cond
|
||||
[(null? sgs) '()]
|
||||
[(>= (event-start-time (segment-event (car sgs))) real-time) sgs]
|
||||
[else (loop (cdr sgs))])))
|
||||
|
||||
;;segment-edge : segment -> uint
|
||||
(define (segment-edge seg)
|
||||
(define evt (segment-event seg))
|
||||
(if (event-has-duration? evt)
|
||||
(segment-x seg)
|
||||
(+ (segment-x seg) (segment-width seg))))
|
||||
|
||||
;;calc-ticks : (listof segment) float trace -> (listof timeline-tick)
|
||||
(define (calc-ticks segs timeToPixMod tr)
|
||||
(define trace-start (inexact->exact (trace-start-time tr)))
|
||||
(define segs-len (length segs))
|
||||
(define-values (lt lx tks)
|
||||
(for/fold ([last-time trace-start]
|
||||
[last-x 0]
|
||||
[ticks '()]) ([i (in-range 0 (floor (/ (- (trace-end-time tr)
|
||||
trace-start)
|
||||
DEFAULT-TIME-INTERVAL)))])
|
||||
(define tick-time (+ last-time DEFAULT-TIME-INTERVAL))
|
||||
(define tick-rel-time (* (add1 i) DEFAULT-TIME-INTERVAL))
|
||||
(define want-x (+ last-x (* DEFAULT-TIME-INTERVAL timeToPixMod)))
|
||||
(define next-seg (findf (λ (s) (> (event-start-time (segment-event s)) tick-time)) segs))
|
||||
(define most-recent-seg (list-ref segs (sub1 (event-index (segment-event next-seg)))))
|
||||
(define most-recent-evt (segment-event most-recent-seg))
|
||||
(define most-recent-time (inexact->exact (event-start-time most-recent-evt)))
|
||||
(define next-evt (segment-event next-seg))
|
||||
(define next-evt-time (inexact->exact (event-start-time next-evt)))
|
||||
(define most-recent-edge (segment-edge most-recent-seg))
|
||||
(define next-edge (segment-x next-seg))
|
||||
(cond
|
||||
[(= most-recent-time tick-time)
|
||||
(values tick-time
|
||||
(segment-x most-recent-seg)
|
||||
(cons (timeline-tick (segment-x most-recent-seg) tick-time tick-rel-time) ticks))]
|
||||
[(= (segment-x next-seg) (add1 (+ (segment-x most-recent-seg) (segment-width most-recent-seg))))
|
||||
(values tick-time
|
||||
(+ (segment-x most-recent-seg) (segment-width most-recent-seg))
|
||||
(cons (timeline-tick (+ (segment-x most-recent-seg)
|
||||
(segment-width most-recent-seg))
|
||||
tick-time
|
||||
tick-rel-time)
|
||||
ticks))]
|
||||
[else
|
||||
(define start-x (max most-recent-edge last-x))
|
||||
(define start-time (max most-recent-time last-time))
|
||||
(define size-mod (/ (- next-edge start-x) (- next-evt-time start-time)))
|
||||
(define x-offset (ceiling (* (- tick-time start-time) size-mod)))
|
||||
(define tick-x (round (+ start-x x-offset)))
|
||||
(values tick-time
|
||||
tick-x
|
||||
(cons (timeline-tick tick-x tick-time tick-rel-time) ticks))])))
|
||||
tks)
|
||||
|
||||
;;calc-process-timespan-lines : trace (listof segment) -> (listof (uint . uint))
|
||||
(define (calc-process-timespan-lines trace segs)
|
||||
(for/list ([tl (in-list (trace-proc-timelines trace))])
|
||||
(let ([segs (filter (λ (s) (= (process-timeline-proc-id tl)
|
||||
(event-proc-id (segment-event s))))
|
||||
segs)])
|
||||
(cons (segment-x (car segs))
|
||||
(segment-x (last segs))))))
|
||||
|
||||
;;get-first-future-seg : seg -> seg
|
||||
(define (get-first-future-seg seg)
|
||||
(let loop ([cur seg])
|
||||
(if (segment-prev-future-seg cur)
|
||||
(loop (segment-prev-future-seg cur))
|
||||
cur)))
|
||||
|
||||
;;get-first-future-seg-in-region : viewable-region segment -> segment
|
||||
(define (get-first-future-seg-in-region vregion seg)
|
||||
(let loop ([cur seg])
|
||||
(let ([prev (segment-prev-future-seg cur)])
|
||||
(if (not prev)
|
||||
cur
|
||||
(if (not ((seg-in-vregion vregion) prev))
|
||||
cur
|
||||
(loop prev))))))
|
||||
|
||||
;;adjust-work-segs! : (listof segment) -> void
|
||||
(define (adjust-work-segs! segs)
|
||||
(for ([seg (in-list segs)])
|
||||
(case (event-type (segment-event seg))
|
||||
[(start-work start-0-work)
|
||||
(set-segment-width! seg (max MIN-SEG-WIDTH
|
||||
(- (segment-x (segment-next-proc-seg seg)) (segment-x seg))))]
|
||||
[else
|
||||
void])))
|
||||
|
||||
;;connect-segments! : (listof segment) -> void
|
||||
(define (connect-segments! segs)
|
||||
(for ([s (in-list segs)])
|
||||
(let ([evt (segment-event s)])
|
||||
(set-segment-prev-proc-seg! s (if (event-prev-proc-event evt)
|
||||
(event-segment (event-prev-proc-event evt))
|
||||
#f))
|
||||
(set-segment-next-proc-seg! s (if (event-next-proc-event evt)
|
||||
(event-segment (event-next-proc-event evt))
|
||||
#f))
|
||||
(set-segment-prev-future-seg! s (if (event-prev-future-event evt)
|
||||
(event-segment (event-prev-future-event evt))
|
||||
#f))
|
||||
(set-segment-next-future-seg! s (if (event-next-future-event evt)
|
||||
(event-segment (event-next-future-event evt))
|
||||
#f))
|
||||
(set-segment-prev-targ-future-seg! s (if (event-prev-targ-future-event evt)
|
||||
(event-segment (event-prev-targ-future-event evt))
|
||||
#f))
|
||||
(set-segment-next-targ-future-seg! s (if (event-next-targ-future-event evt)
|
||||
(event-segment (event-next-targ-future-event evt))
|
||||
#f)))))
|
||||
|
||||
(struct acc (delta last-right-edge) #:transparent)
|
||||
|
||||
;;build-seg-layout : flonum (listof event) trace -> (values (listof segment) uint uint)
|
||||
(define (build-seg-layout timeToPixModifier events tr)
|
||||
(define last-right-edges (build-vector (length (trace-proc-timelines tr)) (λ (n) 0)))
|
||||
(define-values (sgs d x-extent)
|
||||
(for/fold ([segs '()]
|
||||
[delta 0]
|
||||
[largest-x 0]) ([evt (in-list events)])
|
||||
(define last-right-edge (vector-ref last-right-edges (event-proc-index evt)))
|
||||
(define wanted-offset (+ delta (* DEFAULT-TIMELINE-WIDTH
|
||||
(inexact->exact
|
||||
(/ (- (event-start-time evt) (trace-start-time tr))
|
||||
(- (trace-end-time tr) (trace-start-time tr)))))))
|
||||
(define-values (offset new-delta)
|
||||
(if (last-right-edge . <= . wanted-offset)
|
||||
(values wanted-offset delta)
|
||||
(values last-right-edge (+ delta (- last-right-edge wanted-offset)))))
|
||||
(define radius (/ MIN-SEG-WIDTH 2))
|
||||
(define segw MIN-SEG-WIDTH)
|
||||
#;(define segw (case (event-type evt)
|
||||
[(start-work start-0-work)
|
||||
(max MIN-SEG-WIDTH
|
||||
(inexact->exact
|
||||
(round (* timeToPixModifier
|
||||
(- (event-end-time evt) (event-start-time evt))))))]
|
||||
[else MIN-SEG-WIDTH]))
|
||||
(define seg (segment evt
|
||||
(round offset)
|
||||
(- (calc-row-mid-y (event-proc-index evt) TIMELINE-ROW-HEIGHT) radius)
|
||||
segw
|
||||
MIN-SEG-WIDTH
|
||||
(get-event-color (event-type evt))
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f
|
||||
#f))
|
||||
(set-event-segment! evt seg)
|
||||
(vector-set! last-right-edges (event-proc-index evt) (+ offset segw))
|
||||
(values (cons seg segs)
|
||||
new-delta
|
||||
(max largest-x last-right-edge))))
|
||||
(values sgs x-extent))
|
||||
|
||||
;;calc-segments : trace uint uint -> (values frame-info (listof segment))
|
||||
(define (calc-segments tr)
|
||||
(define evts (trace-all-events tr))
|
||||
(define timeToPixModifier (/ DEFAULT-TIMELINE-WIDTH (- (trace-end-time tr) (trace-start-time tr))))
|
||||
(define-values (segments x)
|
||||
(build-seg-layout timeToPixModifier evts tr))
|
||||
(define ordered-segs (reverse segments))
|
||||
(connect-segments! ordered-segs)
|
||||
(adjust-work-segs! ordered-segs)
|
||||
(define ticks (calc-ticks ordered-segs timeToPixModifier tr))
|
||||
(values (frame-info (+ MIN-SEG-WIDTH (round x))
|
||||
(* TIMELINE-ROW-HEIGHT (length (trace-proc-timelines tr)))
|
||||
TIMELINE-ROW-HEIGHT
|
||||
timeToPixModifier
|
||||
ticks
|
||||
(calc-process-timespan-lines tr ordered-segs))
|
||||
ordered-segs))
|
||||
|
||||
;;pict-for-segment : segment -> pict
|
||||
(define (pict-for-segment seg)
|
||||
(when (not (segment-p seg))
|
||||
(set-segment-p! seg (if (event-has-duration? (segment-event seg))
|
||||
(rect-pict (segment-color seg)
|
||||
(timeline-event-strokecolor)
|
||||
(segment-width seg)
|
||||
MIN-SEG-WIDTH
|
||||
#:stroke-width .5)
|
||||
(circle-pict (segment-color seg)
|
||||
(timeline-event-strokecolor)
|
||||
MIN-SEG-WIDTH
|
||||
#:stroke-width .5))))
|
||||
(segment-p seg))
|
||||
|
||||
;;draw-ruler-on : pict viewable-region frameinfo -> pict
|
||||
(define (draw-ruler-on base vregion frameinfo)
|
||||
(let loop ([pct base]
|
||||
[ticks (filter (λ (t) (in-viewable-region-horiz vregion (timeline-tick-x t)))
|
||||
(frame-info-timeline-ticks frameinfo))]
|
||||
[next-label-x (viewable-region-x-extent vregion)]
|
||||
[next-tick-x (viewable-region-x-extent vregion)])
|
||||
(cond
|
||||
[(null? ticks) pct]
|
||||
[(< (- next-tick-x (timeline-tick-x (car ticks))) TIMELINE-MIN-TICK-PADDING)
|
||||
(loop pct
|
||||
(cdr ticks)
|
||||
next-label-x
|
||||
next-tick-x)]
|
||||
[else (let* ([LABEL-PAD 2]
|
||||
[VERT-PAD 3]
|
||||
[cur-tick (car ticks)]
|
||||
[cur-x (timeline-tick-x cur-tick)]
|
||||
[tick-desc (format "~a ms" (real->decimal-string
|
||||
(timeline-tick-rel-time cur-tick) 1))]
|
||||
[t (text-block-pict tick-desc
|
||||
#:backcolor (timeline-tick-label-backcolor)
|
||||
#:forecolor (timeline-tick-label-forecolor)
|
||||
#:padding 3)]
|
||||
[text-width (pict-width t)]
|
||||
[show-label? (<= (+ cur-x LABEL-PAD text-width) next-label-x)]
|
||||
[pinnedline (pin-over pct
|
||||
(- cur-x (viewable-region-x vregion))
|
||||
0
|
||||
(linestyle 'dot
|
||||
(colorize (vline 1
|
||||
(frame-info-adjusted-height frameinfo))
|
||||
(if show-label?
|
||||
(timeline-tick-bold-color)
|
||||
(timeline-tick-color)))))])
|
||||
(if show-label?
|
||||
(loop (pin-over pinnedline
|
||||
(- cur-x (viewable-region-x vregion))
|
||||
VERT-PAD
|
||||
t)
|
||||
(cdr ticks)
|
||||
cur-x
|
||||
cur-x)
|
||||
(loop pinnedline
|
||||
(cdr ticks)
|
||||
next-label-x
|
||||
cur-x)))])))
|
||||
|
||||
;;draw-row-lines-on : pict viewable-region trace frameinfo -> pict
|
||||
(define (draw-row-lines-on base vregion tr finfo opacity)
|
||||
(pin-over base
|
||||
0
|
||||
0
|
||||
(for/fold ([pct base]) ([tl (in-list (trace-proc-timelines tr))]
|
||||
[i (in-naturals)])
|
||||
(let* ([line-coords (list-ref (frame-info-process-line-coords finfo)
|
||||
(process-timeline-proc-index tl))]
|
||||
[line-start (car line-coords)]
|
||||
[line-end (cdr line-coords)]
|
||||
[vregion-start (viewable-region-x vregion)]
|
||||
[vregion-end (viewable-region-x-extent vregion)]
|
||||
[start-x (cond
|
||||
[(< line-start vregion-start) 0]
|
||||
[(between line-start vregion-start vregion-end)
|
||||
(- line-start vregion-start)]
|
||||
[else vregion-end])]
|
||||
[end-x (cond
|
||||
[(< line-end vregion-start) 0]
|
||||
[(between line-end vregion-start vregion-end)
|
||||
(- line-end vregion-start)]
|
||||
[else vregion-end])]
|
||||
[proc-name (if (zero? i)
|
||||
"Thread 0 (Runtime Thread)"
|
||||
(format "Thread ~a" (process-timeline-proc-id tl)))]
|
||||
[proc-title (text-block-pict proc-name
|
||||
#:backcolor (header-backcolor)
|
||||
#:forecolor (header-forecolor)
|
||||
#:padding HEADER-PADDING
|
||||
#:opacity opacity
|
||||
#:width (viewable-region-width vregion))])
|
||||
(draw-stack-onto pct
|
||||
(at 0
|
||||
(- (* (add1 i) (frame-info-row-height finfo)) (viewable-region-y vregion))
|
||||
(colorize (hline (viewable-region-width vregion) 1) (timeline-baseline-color)))
|
||||
(at 0
|
||||
(+ (+ (- (* i (frame-info-row-height finfo)) (viewable-region-y vregion))
|
||||
(- (frame-info-row-height finfo) (pict-height proc-title)))
|
||||
1)
|
||||
proc-title)
|
||||
(at start-x
|
||||
(- (calc-row-mid-y (process-timeline-proc-index tl) (frame-info-row-height finfo))
|
||||
(viewable-region-y vregion))
|
||||
(colorize (hline (- end-x start-x) 1)
|
||||
(timeline-event-baseline-color))))))))
|
||||
|
||||
;Magnifies a segment's pict (dot or square) to make
|
||||
;it stand out when hovered over with the mouse pointer.
|
||||
;;make-stand-out-pict : segment -> pict
|
||||
(define (make-stand-out-pict seg)
|
||||
(case (event-type (segment-event seg))
|
||||
[(start-work start-0-work) (scale (segment-p seg) 1 2)]
|
||||
[else (scale (segment-p seg) 2)]))
|
||||
|
||||
;;frame-bg : viewable-region frame-info trace -> pict
|
||||
(define (frame-bg vregion finfo tr)
|
||||
(draw-frame-bg-onto (colorize (filled-rectangle (viewable-region-width vregion)
|
||||
(frame-info-adjusted-height finfo))
|
||||
(timeline-frame-bg-color))
|
||||
vregion
|
||||
finfo
|
||||
tr
|
||||
TIMELINE-HEADER-OPACITY))
|
||||
|
||||
;;draw-frame-bg-onto : pict viewable-region frameinfo trace -> pict
|
||||
(define (draw-frame-bg-onto base vregion finfo tr opacity)
|
||||
(let ([with-ruler (draw-ruler-on base vregion finfo)])
|
||||
(draw-row-lines-on with-ruler vregion tr finfo opacity)))
|
||||
|
||||
;;draw-connection : viewable-region segment segment pict string [uint bool symbol] -> pict
|
||||
(define (draw-connection vregion
|
||||
start
|
||||
end
|
||||
base-pct
|
||||
color
|
||||
#:width [width 1]
|
||||
#:with-arrow [with-arrow #f]
|
||||
#:style [style 'solid])
|
||||
(let*-values ([(midx midy) (calc-center (- (segment-x start) (viewable-region-x vregion))
|
||||
(- (segment-y start) (viewable-region-y vregion))
|
||||
MIN-SEG-WIDTH)]
|
||||
[(nextx nexty) (calc-center (- (segment-x end) (viewable-region-x vregion))
|
||||
(- (segment-y end) (viewable-region-y vregion))
|
||||
MIN-SEG-WIDTH)]
|
||||
[(dx dy) (values (- nextx midx) (- nexty midy))])
|
||||
(if (and (zero? dy)
|
||||
(or (not (eq? (segment-next-proc-seg start) end))
|
||||
(< dx CONNECTION-LINE-HAT-THRESHOLD)))
|
||||
(let* ([dxa (/ dx 2)]
|
||||
[dya (- HAT-HEIGHT CONNECTION-LINE-HAT-THRESHOLD)]
|
||||
[breakx (+ midx dxa)]
|
||||
[breaky (+ midy dya)])
|
||||
(draw-line-onto (draw-line-onto base-pct
|
||||
midx
|
||||
midy
|
||||
breakx
|
||||
breaky
|
||||
color
|
||||
#:width width
|
||||
#:style style)
|
||||
breakx
|
||||
breaky
|
||||
nextx
|
||||
nexty
|
||||
color
|
||||
#:width width
|
||||
#:with-arrow with-arrow
|
||||
#:style style))
|
||||
(draw-line-onto base-pct
|
||||
midx
|
||||
midy
|
||||
nextx
|
||||
nexty
|
||||
color
|
||||
#:width width
|
||||
#:with-arrow with-arrow
|
||||
#:style style))))
|
||||
|
||||
;;draw-arrows : pict viewable-region segment -> pict
|
||||
(define (draw-arrows base-pct vregion seg)
|
||||
(let ([fst (get-first-future-seg-in-region vregion seg)])
|
||||
(let loop ([pct base-pct]
|
||||
[cur-seg fst])
|
||||
(if (not cur-seg)
|
||||
pct
|
||||
(let ([next (segment-next-future-seg cur-seg)])
|
||||
(let* ([next-targ (segment-next-targ-future-seg cur-seg)]
|
||||
[prev-targ (segment-prev-targ-future-seg cur-seg)]
|
||||
[ftl-arrows (if (not next)
|
||||
pct
|
||||
(draw-connection vregion
|
||||
cur-seg
|
||||
next
|
||||
pct
|
||||
(event-connection-line-color)
|
||||
#:width 2))]
|
||||
[prev-targ-arr (if (not prev-targ)
|
||||
ftl-arrows
|
||||
(draw-connection vregion
|
||||
prev-targ
|
||||
cur-seg
|
||||
ftl-arrows
|
||||
(event-target-future-line-color)
|
||||
#:with-arrow #t
|
||||
#:style 'dot))]
|
||||
[next-targ-arr (if (not next-targ)
|
||||
prev-targ-arr
|
||||
(draw-connection vregion
|
||||
cur-seg
|
||||
next-targ
|
||||
prev-targ-arr
|
||||
(event-target-future-line-color)
|
||||
#:with-arrow #t
|
||||
#:style 'dot))])
|
||||
(loop next-targ-arr
|
||||
next)))))))
|
||||
|
||||
;;timeline-bmp-from-log : (listof indexed-fevent) (or uint bool) (or uint bool) -> bitmap%
|
||||
(define (build-timeline-bmp-from-log logs
|
||||
#:max-width [max-width #f]
|
||||
#:max-height [max-height #f])
|
||||
(define vregion (if (or (not max-width) (not max-height))
|
||||
#f
|
||||
(viewable-region 0
|
||||
0
|
||||
max-width
|
||||
max-height)))
|
||||
|
||||
(define p (build-timeline-pict-from-log logs vregion))
|
||||
(pict->bitmap p))
|
||||
|
||||
(define (truncate-bmp bmp width height)
|
||||
(define w (min width (send bmp get-width)))
|
||||
(define h (min height (send bmp get-height)))
|
||||
(let ([buf (make-bytes (* width height 4))])
|
||||
(send bmp
|
||||
get-argb-pixels
|
||||
0
|
||||
0
|
||||
w
|
||||
h
|
||||
buf)
|
||||
(let ([new-b (make-bitmap w h)])
|
||||
(send new-b
|
||||
set-argb-pixels
|
||||
0
|
||||
0
|
||||
w
|
||||
h
|
||||
buf)
|
||||
new-b)))
|
||||
|
||||
;;build-timeline-bmp-with-overlay : (listof indexed-fevent) uint [uint] [uint] -> bitmap%
|
||||
(define (build-timeline-bmp-with-overlay logs
|
||||
event-index
|
||||
#:max-width [max-width #f]
|
||||
#:max-height [max-height #f])
|
||||
(define p (build-timeline-with-overlay logs event-index))
|
||||
(define-values (w h)
|
||||
(values (if max-width (min max-width (pict-width p)) (pict-width p))
|
||||
(if max-height (min max-height (pict-height p)) (pict-height p))))
|
||||
(truncate-bmp (pict->bitmap p) w h))
|
||||
|
||||
|
||||
;;build-timeline-pict-from-trace : trace viewable-region -> pict
|
||||
(define (build-timeline-pict-from-trace tr vregion)
|
||||
(define-values (finfo segments) (calc-segments tr))
|
||||
(build-timeline-pict vregion
|
||||
tr
|
||||
finfo
|
||||
segments))
|
||||
|
||||
;;build-timeline-pict : (or viewable-region #f) trace frame-info (listof segment) -> pict
|
||||
(define (build-timeline-pict vregion tr finfo segments)
|
||||
(define vr (if (not vregion)
|
||||
(viewable-region 0
|
||||
0
|
||||
(frame-info-adjusted-width finfo)
|
||||
(frame-info-adjusted-height finfo))
|
||||
vregion))
|
||||
(for/fold ([pct (frame-bg vr finfo tr)])
|
||||
([seg (in-list (filter (seg-in-vregion vr) segments))])
|
||||
(pin-over pct
|
||||
(- (segment-x seg) (viewable-region-x vr))
|
||||
(- (segment-y seg) (viewable-region-y vr))
|
||||
(pict-for-segment seg))))
|
||||
|
||||
;;build-timeline-pict-from-log : (listof indexed-fevent) viewable-region -> pict
|
||||
(define (build-timeline-pict-from-log logs vregion)
|
||||
(build-timeline-pict-from-trace (build-trace logs) vregion))
|
||||
|
||||
;;build-timeline-with-overlay : (listof indexed-fevent) uint -> pict
|
||||
(define (build-timeline-with-overlay logs event-index)
|
||||
(define tr (build-trace logs))
|
||||
(define-values (finfo segments) (calc-segments tr))
|
||||
(define vregion (viewable-region-from-frame finfo))
|
||||
(define timeline-p (build-timeline-pict vregion
|
||||
tr
|
||||
finfo
|
||||
segments))
|
||||
(define overlay (build-timeline-overlay vregion
|
||||
#f
|
||||
(list-ref segments event-index)
|
||||
finfo
|
||||
tr))
|
||||
(pin-over timeline-p
|
||||
0
|
||||
0
|
||||
overlay))
|
||||
|
||||
;Draws the pict that is layered on top of the exec. timeline canvas
|
||||
;to highlight a specific future's event sequence
|
||||
;;build-timeline-overlay : uint uint (or segment #f) (or segment #f) frame-info trace -> pict
|
||||
(define (build-timeline-overlay vregion tacked hovered finfo tr)
|
||||
(define-values (width height) (values (viewable-region-width vregion)
|
||||
(viewable-region-height vregion)))
|
||||
(define base (blank (viewable-region-width vregion)
|
||||
(viewable-region-height vregion)))
|
||||
(define-values (seg-with-arrows showing-tacked)
|
||||
(if tacked (values tacked #t) (values hovered #f)))
|
||||
(if (and seg-with-arrows (segment-p seg-with-arrows))
|
||||
(let* ([bg base]
|
||||
[dots (let loop ([p bg] [cur-seg (get-first-future-seg-in-region vregion seg-with-arrows)])
|
||||
(if (or (not cur-seg) (not ((seg-in-vregion vregion) cur-seg)))
|
||||
p
|
||||
(loop (pin-over p
|
||||
(- (segment-x cur-seg) (viewable-region-x vregion))
|
||||
(- (segment-y cur-seg) (viewable-region-y vregion))
|
||||
(pict-for-segment cur-seg))
|
||||
(segment-next-future-seg cur-seg))))]
|
||||
[aseg-rel-x (- (segment-x seg-with-arrows) (viewable-region-x vregion))]
|
||||
[aseg-rel-y (- (segment-y seg-with-arrows) (viewable-region-y vregion))]
|
||||
[line (pin-over dots
|
||||
(- (+ aseg-rel-x
|
||||
(/ (segment-width seg-with-arrows) 2))
|
||||
2)
|
||||
0
|
||||
(colorize (vline 1 height) (hover-tickline-color)))]
|
||||
[bigger (make-stand-out-pict seg-with-arrows)]
|
||||
[width-dif (/ (- (pict-width bigger) (pict-width (segment-p seg-with-arrows))) 2)]
|
||||
[height-dif (/ (- (pict-height bigger) (pict-height (segment-p seg-with-arrows))) 2)]
|
||||
[magnified (pin-over line
|
||||
(- aseg-rel-x width-dif)
|
||||
(- aseg-rel-y height-dif)
|
||||
bigger)]
|
||||
[hover-magnified (if (and showing-tacked
|
||||
hovered
|
||||
(segment-p hovered)
|
||||
(not (eq? hovered tacked)))
|
||||
(let* ([hmag (make-stand-out-pict hovered)]
|
||||
[hwidth-dif (/ (- (pict-width hmag)
|
||||
(pict-width (segment-p hovered)))
|
||||
2)]
|
||||
[hheight-dif (/ (- (pict-height hmag)
|
||||
(pict-height (segment-p hovered)))
|
||||
2)])
|
||||
(pin-over magnified
|
||||
(- (- (segment-x hovered) (viewable-region-x vregion)) hwidth-dif)
|
||||
(- (- (segment-y hovered) (viewable-region-y vregion)) hheight-dif)
|
||||
hmag))
|
||||
magnified)]
|
||||
[arrows (draw-arrows hover-magnified vregion seg-with-arrows)])
|
||||
arrows)
|
||||
base))
|
||||
|
||||
;Draw a line from one node on the creation graph to another
|
||||
;;line-from : drawable-node drawable-node pict viewable-region -> pict
|
||||
(define (line-from start end pct vregion)
|
||||
(let* ([par-center (drawable-node-center start)]
|
||||
[child-center (drawable-node-center end)]
|
||||
[minx (viewable-region-x vregion)]
|
||||
[miny (viewable-region-y vregion)])
|
||||
(draw-line-onto pct
|
||||
(- (point-x par-center) minx)
|
||||
(- (point-y par-center) miny)
|
||||
(- (point-x child-center) minx)
|
||||
(- (point-y child-center) miny)
|
||||
(create-graph-edge-color)
|
||||
#:width 1
|
||||
#:style 'dot)))
|
||||
|
||||
;Draws a circle for a node on the creation graph
|
||||
;;node-pict : drawable-node -> pict
|
||||
(define (node-pict dnode)
|
||||
(let* ([ndata (node-data (drawable-node-node dnode))]
|
||||
[ntext (if (equal? ndata 'runtime-thread)
|
||||
"RTT"
|
||||
(format "~a" (event-user-data ndata)))])
|
||||
(cc-superimpose (circle-pict (create-graph-node-backcolor)
|
||||
(create-graph-node-strokecolor)
|
||||
(drawable-node-width dnode))
|
||||
(colorize (text ntext) (create-graph-node-forecolor)))))
|
||||
|
||||
;Cache the creation graph pict after first drawing
|
||||
(define cg-pict #f)
|
||||
|
||||
;;draw-creategraph-pict : viewable-region tree-layout -> pict
|
||||
(define (draw-creategraph-pict vregion layout)
|
||||
(define rt-root (first (graph-layout-nodes layout)))
|
||||
(define width (inexact->exact (floor (graph-layout-width layout))))
|
||||
(define height (inexact->exact (floor (graph-layout-height layout))))
|
||||
(define base (blank (viewable-region-width vregion) (viewable-region-height vregion)))
|
||||
(define viewable-nodes (filter (λ (n) (in-viewable-region vregion
|
||||
(drawable-node-x n)
|
||||
(drawable-node-y n)
|
||||
(drawable-node-width n)
|
||||
(drawable-node-width n)))
|
||||
(graph-layout-nodes layout)))
|
||||
(let ([arrow-pct (for/fold ([pct base]) ([node (in-list (graph-layout-nodes layout))])
|
||||
(for/fold ([p pct]) ([child (in-list (drawable-node-children node))])
|
||||
(line-from node child p vregion)))])
|
||||
(for/fold ([pct arrow-pct]) ([node (in-list viewable-nodes)])
|
||||
(pin-over pct
|
||||
(- (drawable-node-x node) (viewable-region-x vregion))
|
||||
(- (drawable-node-y node) (viewable-region-y vregion))
|
||||
(node-pict node)))))
|
||||
|
||||
;Draws the pict displayed in the creation graph canvas
|
||||
;;creategraph-p : viewable-region tree-layout uint -> pict
|
||||
(define (build-creategraph-pict vregion layout zoom-level)
|
||||
(define factor (zoom-level->factor zoom-level))
|
||||
(set! cg-pict (draw-creategraph-pict (scale-viewable-region vregion (/ 1 factor)) layout))
|
||||
(scale cg-pict factor))
|
||||
|
||||
(define (zoom-level->factor zoom-level)
|
||||
(+ 1.0 (* (- zoom-level CREATE-GRAPH-DEFAULT-ZOOM)
|
||||
CREATE-GRAPH-ZOOM-FACTOR)))
|
||||
|
||||
;;graph-overlay-pict : drawable-node trace graph-layout -> pict
|
||||
(define (graph-overlay-pict hover-node tr layout vregion)
|
||||
(when hover-node
|
||||
(unless (equal? (node-data (drawable-node-node hover-node)) 'runtime-thread)
|
||||
(define fid (event-user-data (node-data (drawable-node-node hover-node))))
|
||||
(define ri (hash-ref (trace-future-rtcalls tr) fid (λ () #f)))
|
||||
(when ri
|
||||
(define block-ops (sort (hash-keys (rtcall-info-block-hash ri))
|
||||
>
|
||||
#:key (λ (p)
|
||||
(hash-ref (rtcall-info-block-hash ri) p))))
|
||||
(define sync-ops (sort (hash-keys (rtcall-info-sync-hash ri))
|
||||
>
|
||||
#:key (λ (op)
|
||||
(hash-ref (rtcall-info-sync-hash ri) op))))
|
||||
(define-values (node-origin-x node-origin-y)
|
||||
(values (- (drawable-node-x hover-node) (viewable-region-x vregion))
|
||||
(- (drawable-node-y hover-node) (viewable-region-y vregion))))
|
||||
(define-values (center-x center-y)
|
||||
(values (+ node-origin-x (/ (drawable-node-width hover-node) 2))
|
||||
(+ node-origin-y (/ (drawable-node-width hover-node) 2))))
|
||||
(define x (+ center-x CREATE-GRAPH-NODE-DIAMETER))
|
||||
(define-values (pct yacc)
|
||||
(for/fold ([p (pin-over (blank (viewable-region-width vregion) (viewable-region-height vregion))
|
||||
node-origin-x
|
||||
node-origin-y
|
||||
(node-pict hover-node))]
|
||||
[yacc node-origin-y])
|
||||
([rtcall (in-list (append (map (λ (op) (cons 'block op)) block-ops)
|
||||
(map (λ (op) (cons 'sync op)) sync-ops)))])
|
||||
(define evt-type (car rtcall))
|
||||
(define prim (cdr rtcall))
|
||||
(define the-hash (if (equal? evt-type 'block) (rtcall-info-block-hash ri) (rtcall-info-sync-hash ri)))
|
||||
(define txtp (text-pict (format "~a (~a)"
|
||||
(symbol->string prim)
|
||||
(hash-ref the-hash prim))
|
||||
#:color (get-event-forecolor evt-type)))
|
||||
(define txtbg (rect-pict (get-event-color evt-type)
|
||||
(create-graph-edge-color)
|
||||
(+ (pict-width txtp) (* TOOLTIP-MARGIN 2))
|
||||
(+ (pict-height txtp) (* TOOLTIP-MARGIN 2))
|
||||
#:stroke-width .5))
|
||||
(values
|
||||
(pin-over (draw-line-onto p
|
||||
center-x
|
||||
center-y
|
||||
x
|
||||
yacc
|
||||
(create-graph-edge-color))
|
||||
x
|
||||
yacc
|
||||
(pin-over txtbg
|
||||
TOOLTIP-MARGIN
|
||||
TOOLTIP-MARGIN
|
||||
txtp))
|
||||
(+ yacc (pict-height txtbg) CREATE-GRAPH-PADDING))))
|
||||
pct))))
|
||||
|
361
collects/racket/future/private/visualizer-gui.rkt
Normal file
361
collects/racket/future/private/visualizer-gui.rkt
Normal file
|
@ -0,0 +1,361 @@
|
|||
#lang racket/gui
|
||||
(require framework
|
||||
data/interval-map
|
||||
mrlib/hierlist
|
||||
"visualizer-drawing.rkt"
|
||||
"visualizer-data.rkt"
|
||||
"gui-helpers.rkt"
|
||||
"graph-drawing.rkt"
|
||||
"display.rkt"
|
||||
"constants.rkt")
|
||||
|
||||
(provide (contract-out [show-visualizer (-> void?)])
|
||||
show-visualizer-for-trace)
|
||||
|
||||
;;rebuild-mouse-index : frame-info trace (listof segment) -> interval-map of (range --> interval-map)
|
||||
(define (rebuild-mouse-index frameinfo tr segs)
|
||||
(let ([ym (make-interval-map)])
|
||||
(for ([tl (in-list (trace-proc-timelines tr))])
|
||||
(let* ([xm (make-interval-map)]
|
||||
[midy (calc-row-mid-y (process-timeline-proc-index tl) (frame-info-row-height frameinfo))]
|
||||
[miny (floor (- midy (/ MIN-SEG-WIDTH 2)))]
|
||||
[maxy (floor (+ midy (/ MIN-SEG-WIDTH 2)))])
|
||||
(interval-map-set! ym
|
||||
miny
|
||||
maxy
|
||||
xm)
|
||||
(for ([seg (in-list (filter (λ (s)
|
||||
(= (event-proc-id (segment-event s))
|
||||
(process-timeline-proc-id tl)))
|
||||
segs))])
|
||||
(interval-map-set! xm
|
||||
(segment-x seg)
|
||||
(+ (segment-x seg) (segment-width seg))
|
||||
seg))))
|
||||
ym))
|
||||
|
||||
;;display-evt-details : segment trace message message message message message message -> void
|
||||
(define (display-evt-details seg
|
||||
tr
|
||||
selected-label
|
||||
time-label
|
||||
fid-label
|
||||
pid-label
|
||||
data-label1
|
||||
data-label2)
|
||||
(if seg
|
||||
(let ([evt (segment-event seg)])
|
||||
(send selected-label set-label (format "Event: ~a" (event-type evt)))
|
||||
(send time-label set-label (format "Time: +~a" (get-time-string (- (event-start-time evt)
|
||||
(trace-start-time tr)))))
|
||||
(send fid-label set-label (format "Future ID: ~a" (if (not (event-future-id evt))
|
||||
"None (top-level event)"
|
||||
(event-future-id evt))))
|
||||
(send pid-label set-label (format "Process ID: ~a" (event-proc-id evt)))
|
||||
(case (event-type evt)
|
||||
[(start-work start-0-work)
|
||||
(send data-label1 set-label (format "Duration: ~a" (get-time-string (- (event-end-time evt)
|
||||
(event-start-time evt)))))]
|
||||
[(block sync)
|
||||
(when (= (event-proc-id evt) RT-THREAD-ID)
|
||||
(send data-label1 set-label (format "Primitive: ~a" (symbol->string (event-prim-name evt)))))
|
||||
(when (equal? (event-prim-name evt) 'touch)
|
||||
(send data-label2 set-label (format "Touching future ~a" (event-user-data evt))))
|
||||
(when (equal? (event-prim-name evt) (string->symbol "[allocate memory]"))
|
||||
(send data-label2 set-label (format "Size: ~a" (event-user-data evt))))]
|
||||
[(create)
|
||||
(send data-label1 set-label (format "Creating future ~a" (event-user-data evt)))]
|
||||
[(touch)
|
||||
(send data-label1 set-label (format "Touching future ~a" (event-user-data evt)))]
|
||||
[else
|
||||
(send data-label1 set-label "")]))
|
||||
(begin
|
||||
(send selected-label set-label "")
|
||||
(send time-label set-label "")
|
||||
(send fid-label set-label "")
|
||||
(send pid-label set-label "")
|
||||
(send data-label1 set-label "")
|
||||
(send data-label2 set-label ""))))
|
||||
|
||||
(define (get-window-size)
|
||||
(define-values (screen-w screen-h) (get-display-size))
|
||||
(values (min screen-w DEF-WINDOW-WIDTH)
|
||||
(min screen-h DEF-WINDOW-HEIGHT)))
|
||||
|
||||
(define (show-visualizer-for-trace logs)
|
||||
;TODO: Just set initial sizes, not minimum sizes
|
||||
;If for some reason the log is empty, error?
|
||||
(when (empty? logs)
|
||||
(error 'show-visualizer "No future log messages found."))
|
||||
(define the-trace (build-trace logs))
|
||||
(define-values (winw winh) (get-window-size))
|
||||
;The event segment we are currently mousing over
|
||||
(define hover-seg #f)
|
||||
;The event segment we last clicked on (tacked) -- if any
|
||||
(define tacked-seg #f)
|
||||
;Table for broadcasting selection events to other controls
|
||||
(define listener-table (make-listener-table))
|
||||
|
||||
(define f (new frame:standard-menus%
|
||||
[label "Futures Performance"]
|
||||
[width winw]
|
||||
[height winh]))
|
||||
(define main-panel (new panel:horizontal-dragable%
|
||||
[parent (send f get-area-container)]))
|
||||
(define left-panel (new panel:horizontal-dragable% [parent main-panel]
|
||||
[stretchable-width #t]))
|
||||
(define hlist-ctl (new hierarchical-list%
|
||||
[parent left-panel]
|
||||
[stretchable-width #t]
|
||||
[stretchable-height #t]
|
||||
[style '(control-border)]))
|
||||
|
||||
;Build up items in the hierlist
|
||||
(define block-node (send hlist-ctl new-list))
|
||||
(send (send block-node get-editor) insert "Blocks" 0)
|
||||
(for ([prim (in-list (sort (hash-keys (trace-block-counts the-trace)) > #:key (λ (x) (hash-ref (trace-block-counts the-trace) x))))])
|
||||
(define item (send block-node new-item))
|
||||
(send (send item get-editor) insert (format "~a (~a)" prim (hash-ref (trace-block-counts the-trace) prim))))
|
||||
|
||||
(define sync-node (send hlist-ctl new-list))
|
||||
(send (send sync-node get-editor) insert "Syncs" 0)
|
||||
(for ([prim (in-list (sort (hash-keys (trace-sync-counts the-trace)) > #:key (λ (x) (hash-ref (trace-sync-counts the-trace) x))))])
|
||||
(define item (send sync-node new-item))
|
||||
(send (send item get-editor) insert (format "~a (~a)" prim (hash-ref (trace-sync-counts the-trace) prim))))
|
||||
|
||||
(define right-panel (new panel:vertical-dragable%
|
||||
[parent main-panel]
|
||||
[stretchable-width #t]))
|
||||
(define graphic-panel (new panel:horizontal-dragable%
|
||||
[parent right-panel]
|
||||
[stretchable-height #t]
|
||||
[min-width (inexact->exact (round (* winw .8)))]))
|
||||
(define timeline-container (new vertical-panel%
|
||||
[parent graphic-panel]
|
||||
[stretchable-width #t]
|
||||
[stretchable-height #t]))
|
||||
(define graph-container (new vertical-panel%
|
||||
[parent graphic-panel]
|
||||
[stretchable-width #t]
|
||||
[stretchable-height #t]))
|
||||
(define timeline-header (section-header timeline-container "Execution Timeline" 'horizontal))
|
||||
(define graph-header (section-header graph-container "Future Creation Tree" 'horizontal))
|
||||
|
||||
;Calculate required sizes, mouse hover index, and create timeline pict container
|
||||
(define-values (frameinfo segments) (calc-segments the-trace))
|
||||
(define timeline-mouse-index (rebuild-mouse-index frameinfo the-trace segments))
|
||||
(define timeline-panel (new pict-canvas%
|
||||
[parent timeline-container]
|
||||
[redraw-on-resize #f]
|
||||
[pict-builder (λ (vregion) (build-timeline-pict vregion the-trace frameinfo segments))]
|
||||
[hover-handler (λ (x y vregion)
|
||||
(let ([seg (find-seg-for-coords x y timeline-mouse-index)])
|
||||
(set! hover-seg seg)
|
||||
;(send timeline-panel set-redraw-overlay! #t)
|
||||
(post-event listener-table 'segment-hover timeline-panel seg)))]
|
||||
[click-handler (λ (x y vregion)
|
||||
(let ([seg (find-seg-for-coords x y timeline-mouse-index)])
|
||||
(set! tacked-seg seg)
|
||||
;(send timeline-panel set-redraw-overlay! #t)
|
||||
(post-event listener-table 'segment-click timeline-panel seg)))]
|
||||
[overlay-builder (λ (vregion)
|
||||
(build-timeline-overlay vregion
|
||||
tacked-seg
|
||||
hover-seg
|
||||
frameinfo
|
||||
the-trace))]
|
||||
[min-width 500]
|
||||
[min-height (inexact->exact (round (* winh .7)))]
|
||||
[style '(hscroll vscroll)]
|
||||
[stretchable-width #t]))
|
||||
(send timeline-panel init-auto-scrollbars
|
||||
(frame-info-adjusted-width frameinfo)
|
||||
(frame-info-adjusted-height frameinfo)
|
||||
0.0
|
||||
0.0)
|
||||
(send timeline-panel show-scrollbars #t #t)
|
||||
(send timeline-panel set-redo-bitmap-on-paint! #t)
|
||||
|
||||
;Calculate for and create creation graph pict container
|
||||
(define creation-tree-layout (draw-tree (trace-creation-tree the-trace)
|
||||
#:node-width CREATE-GRAPH-NODE-DIAMETER
|
||||
#:padding CREATE-GRAPH-PADDING
|
||||
#:zoom CREATE-GRAPH-DEFAULT-ZOOM))
|
||||
|
||||
(define hovered-graph-node #f)
|
||||
(define creategraph-panel (new pict-canvas%
|
||||
[parent graph-container]
|
||||
[redraw-on-resize #f]
|
||||
[pict-builder (λ (vregion)
|
||||
(build-creategraph-pict vregion
|
||||
creation-tree-layout
|
||||
cg-zoom-level))]
|
||||
[hover-handler (λ (x y vregion)
|
||||
(set! hovered-graph-node
|
||||
(find-node-for-coords x
|
||||
y
|
||||
(graph-layout-nodes creation-tree-layout))))]
|
||||
[click-handler (λ (x y vregion)
|
||||
(define fid (find-fid-for-coords
|
||||
x y (graph-layout-nodes creation-tree-layout)
|
||||
vregion))
|
||||
(when fid
|
||||
(define seg (first-seg-for-fid fid segments))
|
||||
(set! tacked-seg seg)
|
||||
(send timeline-panel set-redraw-overlay! #t)
|
||||
(send timeline-panel refresh)
|
||||
(post-event listener-table 'segment-click timeline-panel seg)))]
|
||||
[overlay-builder (λ (vregion)
|
||||
(graph-overlay-pict hovered-graph-node
|
||||
the-trace
|
||||
creation-tree-layout
|
||||
vregion))]
|
||||
[min-width 500]
|
||||
[min-height 500]
|
||||
[style '(hscroll vscroll)]
|
||||
[stretchable-width #t]))
|
||||
|
||||
(send creategraph-panel show-scrollbars #t #t)
|
||||
(send creategraph-panel init-auto-scrollbars
|
||||
(inexact->exact (floor (graph-layout-width creation-tree-layout)))
|
||||
(inexact->exact (floor (graph-layout-height creation-tree-layout)))
|
||||
0.0
|
||||
0.0)
|
||||
(send creategraph-panel set-redo-bitmap-on-paint! #t)
|
||||
|
||||
|
||||
(define graph-footer (new horizontal-panel%
|
||||
[parent graph-container]
|
||||
[stretchable-width #t]
|
||||
[stretchable-height #f]
|
||||
[style '(border)]))
|
||||
(define cg-zoom-level CREATE-GRAPH-DEFAULT-ZOOM)
|
||||
|
||||
;;Handles a change event for the creation graph zoom slider
|
||||
;;on-zoom : slider% event% -> void
|
||||
(define (on-zoom slider event)
|
||||
(printf "slider: ~s\n" (send slider get-value)) ;;REMOVE
|
||||
(set! cg-zoom-level (send slider get-value))
|
||||
(send creategraph-panel redraw-everything))
|
||||
|
||||
(define zoom-slider (new slider%
|
||||
[parent graph-footer]
|
||||
[label "Zoom:"]
|
||||
[min-value CREATE-GRAPH-MIN-ZOOM]
|
||||
[max-value CREATE-GRAPH-MAX-ZOOM]
|
||||
[init-value CREATE-GRAPH-DEFAULT-ZOOM]
|
||||
[style '(horizontal plain)]
|
||||
[callback on-zoom]))
|
||||
(define bottom-panel (new horizontal-panel%
|
||||
[parent right-panel]
|
||||
[style '(border)]
|
||||
[stretchable-height #t]))
|
||||
|
||||
(define left-container (new horizontal-panel%
|
||||
[parent bottom-panel]
|
||||
[style '(border)]
|
||||
[stretchable-height #t]))
|
||||
(define mid-container (new horizontal-panel%
|
||||
[parent bottom-panel]
|
||||
[stretchable-height #t]))
|
||||
(define right-container (new horizontal-panel%
|
||||
[parent bottom-panel]
|
||||
[stretchable-height #t]))
|
||||
(define left-bot-header (section-header left-container "Execution Statistics" 'vertical))
|
||||
(define left-bot-panel (new vertical-panel%
|
||||
[parent left-container]
|
||||
[stretchable-height #t]))
|
||||
(define mid-bot-header (section-header mid-container "Event Details" 'vertical))
|
||||
(define mid-bot-panel (new vertical-panel%
|
||||
[parent mid-container]
|
||||
[stretchable-height #t]))
|
||||
(define right-bot-header (section-header right-container "Log Viewer" 'vertical))
|
||||
(define right-bot-panel (new vertical-panel%
|
||||
[parent right-container]
|
||||
[stretchable-height #t]))
|
||||
|
||||
(bold-label left-bot-panel "Program Statistics")
|
||||
(define runtime-label (label left-bot-panel
|
||||
(format "Real time: ~a" (get-time-string (trace-real-time the-trace)))))
|
||||
(define fcount-label (label left-bot-panel
|
||||
(format "Total futures: ~a" (trace-num-futures the-trace))))
|
||||
(define blocks-label (label left-bot-panel
|
||||
(format "Barricades: ~a" (trace-num-blocks the-trace))))
|
||||
(define syncs-label (label left-bot-panel
|
||||
(format "Syncs: ~a" (trace-num-syncs the-trace))))
|
||||
|
||||
;Selected-event-specific labels
|
||||
(define hover-label (mt-bold-label mid-bot-panel))
|
||||
(define hover-time-label (mt-label mid-bot-panel))
|
||||
(define hover-fid-label (mt-label mid-bot-panel))
|
||||
(define hover-pid-label (mt-label mid-bot-panel))
|
||||
(define hover-data-label1 (mt-label mid-bot-panel))
|
||||
(define hover-data-label2 (mt-label mid-bot-panel))
|
||||
|
||||
(define tacked-label (mt-bold-label mid-bot-panel))
|
||||
(define tacked-time-lbl (mt-label mid-bot-panel))
|
||||
(define tacked-fid-lbl (mt-label mid-bot-panel))
|
||||
(define tacked-pid-lbl (mt-label mid-bot-panel))
|
||||
(define tacked-data-lbl (mt-label mid-bot-panel))
|
||||
(define tacked-data-lbl2 (mt-label mid-bot-panel))
|
||||
|
||||
(define (update-event-details-panel seg)
|
||||
(display-evt-details hover-seg
|
||||
the-trace
|
||||
hover-label
|
||||
hover-time-label
|
||||
hover-fid-label
|
||||
hover-pid-label
|
||||
hover-data-label1
|
||||
hover-data-label2)
|
||||
(display-evt-details tacked-seg
|
||||
the-trace
|
||||
tacked-label
|
||||
tacked-time-lbl
|
||||
tacked-fid-lbl
|
||||
tacked-pid-lbl
|
||||
tacked-data-lbl
|
||||
tacked-data-lbl2))
|
||||
|
||||
;Wire up events so selection, etc. in one panel is communicated to others
|
||||
(define (on-future-selected fid)
|
||||
0)
|
||||
|
||||
(define (on-segment-hover seg)
|
||||
0)
|
||||
|
||||
(define (on-segment-click seg)
|
||||
0)
|
||||
|
||||
(define (on-segment-unclick seg)
|
||||
0)
|
||||
|
||||
;Wire up event handlers for selection, etc.
|
||||
(add-receiver listener-table 'future-selected timeline-panel on-future-selected)
|
||||
(add-receiver listener-table 'segment-hover creategraph-panel on-segment-hover)
|
||||
(add-receiver listener-table 'segment-click creategraph-panel on-segment-click)
|
||||
(add-receiver listener-table 'segment-click mid-bot-panel update-event-details-panel)
|
||||
(add-receiver listener-table 'segment-hover mid-bot-panel update-event-details-panel)
|
||||
(add-receiver listener-table 'segment-unclick mid-bot-panel update-event-details-panel)
|
||||
(add-receiver listener-table 'segment-unclick creategraph-panel on-segment-unclick)
|
||||
|
||||
;Additional menus/items
|
||||
(define showing-create-graph #t)
|
||||
(define view-menu (new menu% [label "View"] [parent (send f get-menu-bar)]))
|
||||
(new menu-item%
|
||||
[label "Hide Creation Tree"]
|
||||
[parent view-menu]
|
||||
[callback (λ (item evt)
|
||||
(if showing-create-graph
|
||||
(begin
|
||||
(send graphic-panel delete-child graph-container)
|
||||
(send item set-label "Show Creation Tree"))
|
||||
(begin
|
||||
(send graphic-panel add-child graph-container)
|
||||
(send item set-label "Hide Creation Tree")))
|
||||
(set! showing-create-graph (not showing-create-graph)))])
|
||||
|
||||
(send f show #t))
|
||||
|
||||
(define (show-visualizer)
|
||||
(show-visualizer-for-trace (raw-log-output 0)))
|
5
collects/racket/future/visualizer.rkt
Normal file
5
collects/racket/future/visualizer.rkt
Normal file
|
@ -0,0 +1,5 @@
|
|||
#lang racket/base
|
||||
(require "private/visualizer-gui.rkt"
|
||||
"private/visualizer-data.rkt")
|
||||
(provide start-performance-tracking!
|
||||
show-visualizer)
|
|
@ -1,7 +1,12 @@
|
|||
#lang scribble/doc
|
||||
@(require scribble/manual "guide-utils.rkt"
|
||||
@(require scribble/manual scribble/eval "guide-utils.rkt"
|
||||
(for-label racket/flonum racket/future))
|
||||
|
||||
@(define future-eval (make-base-eval))
|
||||
@(interaction-eval #:eval future-eval (require racket/future
|
||||
racket/future/private/visualizer-drawing
|
||||
racket/future/private/visualizer-data))
|
||||
|
||||
@title[#:tag "effective-futures"]{Parallelism with Futures}
|
||||
|
||||
The @racketmodname[racket/future] library provides support for
|
||||
|
@ -56,9 +61,12 @@ l2)] becomes available about the same time that it is demanded by
|
|||
@racket[(touch f)].
|
||||
|
||||
Futures run in parallel as long as they can do so safely, but the
|
||||
notion of ``safe'' for parallelism is inherently tied to the system
|
||||
implementation. The distinction between ``safe'' and ``unsafe''
|
||||
notion of ``future safe'' is inherently tied to the
|
||||
implementation. The distinction between ``future safe'' and ``future unsafe''
|
||||
operations may be far from apparent at the level of a Racket program.
|
||||
The remainder of this section works through an example to illustrate
|
||||
this distinction and to show how to use the future visualizer
|
||||
can help shed light on it.
|
||||
|
||||
Consider the following core of a Mandelbrot-set computation:
|
||||
|
||||
|
@ -72,10 +80,10 @@ Consider the following core of a Mandelbrot-set computation:
|
|||
(let ([zrq (* zr zr)]
|
||||
[ziq (* zi zi)])
|
||||
(cond
|
||||
[(> (+ zrq ziq) 4.0) i]
|
||||
[(> (+ zrq ziq) 4) i]
|
||||
[else (loop (add1 i)
|
||||
(+ (- zrq ziq) cr)
|
||||
(+ (* 2.0 zr zi) ci))]))))))
|
||||
(+ (* 2 zr zi) ci))]))))))
|
||||
]
|
||||
|
||||
The expressions @racket[(mandelbrot 10000000 62 500 1000)] and
|
||||
|
@ -97,35 +105,332 @@ Unfortunately, attempting to run the two computations in parallel with
|
|||
(touch f)))
|
||||
]
|
||||
|
||||
One problem is that the @racket[*] and @racket[/] operations in the
|
||||
first two lines of @racket[mandelbrot] involve a mixture of exact and
|
||||
inexact real numbers. Such mixtures typically trigger a slow path in
|
||||
execution, and the general slow path is not safe for
|
||||
parallelism. Consequently, the future created in this example is
|
||||
almost immediately suspended, and it cannot resume until
|
||||
@racket[touch] is called.
|
||||
To see why, use the @racketmodname[racket/future/visualizer], like this:
|
||||
|
||||
Changing the first two lines of @racket[mandelbrot] addresses that
|
||||
first the problem:
|
||||
@racketblock[
|
||||
(require racket/future/visualizer)
|
||||
(start-performance-tracking!)
|
||||
|
||||
(let ([f (future (lambda () (mandelbrot 10000000 62 501 1000)))])
|
||||
(list (mandelbrot 10000000 62 500 1000)
|
||||
(touch f)))
|
||||
|
||||
(show-visualizer)]
|
||||
|
||||
This opens a window showing a graphical view of a trace of the computation.
|
||||
The upper-left portion of the window contains an execution timeline:
|
||||
|
||||
@(interaction-eval
|
||||
#:eval future-eval
|
||||
(define bad-log
|
||||
(list (indexed-fevent 0 '#s(future-event #f 0 create 1334778390997.936 #f 1))
|
||||
(indexed-fevent 1 '#s(future-event 1 1 start-work 1334778390998.137 #f #f))
|
||||
(indexed-fevent 2 '#s(future-event 1 1 sync 1334778390998.145 #f #f))
|
||||
(indexed-fevent 3 '#s(future-event 1 0 sync 1334778391001.616 [allocate memory] #f))
|
||||
(indexed-fevent 4 '#s(future-event 1 0 result 1334778391001.629 #f #f))
|
||||
(indexed-fevent 5 '#s(future-event 1 1 result 1334778391001.643 #f #f))
|
||||
(indexed-fevent 6 '#s(future-event 1 1 block 1334778391001.653 #f #f))
|
||||
(indexed-fevent 7 '#s(future-event 1 1 suspend 1334778391001.658 #f #f))
|
||||
(indexed-fevent 8 '#s(future-event 1 1 end-work 1334778391001.658 #f #f))
|
||||
(indexed-fevent 9 '#s(future-event 1 0 block 1334778392134.226 > #f))
|
||||
(indexed-fevent 10 '#s(future-event 1 0 result 1334778392134.241 #f #f))
|
||||
(indexed-fevent 11 '#s(future-event 1 1 start-work 1334778392134.254 #f #f))
|
||||
(indexed-fevent 12 '#s(future-event 1 1 sync 1334778392134.339 #f #f))
|
||||
(indexed-fevent 13 '#s(future-event 1 0 sync 1334778392134.375 [allocate memory] #f))
|
||||
(indexed-fevent 14 '#s(future-event 1 0 result 1334778392134.38 #f #f))
|
||||
(indexed-fevent 15 '#s(future-event 1 1 result 1334778392134.387 #f #f))
|
||||
(indexed-fevent 16 '#s(future-event 1 1 block 1334778392134.39 #f #f))
|
||||
(indexed-fevent 17 '#s(future-event 1 1 suspend 1334778392134.391 #f #f))
|
||||
(indexed-fevent 18 '#s(future-event 1 1 end-work 1334778392134.391 #f #f))
|
||||
(indexed-fevent 19 '#s(future-event 1 0 touch-pause 1334778392134.432 #f #f))
|
||||
(indexed-fevent 20 '#s(future-event 1 0 touch-resume 1334778392134.433 #f #f))
|
||||
(indexed-fevent 21 '#s(future-event 1 0 block 1334778392134.533 * #f))
|
||||
(indexed-fevent 22 '#s(future-event 1 0 result 1334778392134.537 #f #f))
|
||||
(indexed-fevent 23 '#s(future-event 1 2 start-work 1334778392134.568 #f #f))
|
||||
(indexed-fevent 24 '#s(future-event 1 2 sync 1334778392134.57 #f #f))
|
||||
(indexed-fevent 25 '#s(future-event 1 0 touch-pause 1334778392134.587 #f #f))
|
||||
(indexed-fevent 26 '#s(future-event 1 0 touch-resume 1334778392134.587 #f #f))
|
||||
(indexed-fevent 27 '#s(future-event 1 0 block 1334778392134.6 [allocate memory] #f))
|
||||
(indexed-fevent 28 '#s(future-event 1 0 result 1334778392134.604 #f #f))
|
||||
(indexed-fevent 29 '#s(future-event 1 2 result 1334778392134.627 #f #f))
|
||||
(indexed-fevent 30 '#s(future-event 1 2 block 1334778392134.629 #f #f))
|
||||
(indexed-fevent 31 '#s(future-event 1 2 suspend 1334778392134.632 #f #f))
|
||||
(indexed-fevent 32 '#s(future-event 1 2 end-work 1334778392134.633 #f #f))
|
||||
(indexed-fevent 33 '#s(future-event 1 0 touch-pause 1334778392134.64 #f #f))
|
||||
(indexed-fevent 34 '#s(future-event 1 0 touch-resume 1334778392134.64 #f #f))
|
||||
(indexed-fevent 35 '#s(future-event 1 0 block 1334778392134.663 > #f))
|
||||
(indexed-fevent 36 '#s(future-event 1 0 result 1334778392134.666 #f #f))
|
||||
(indexed-fevent 37 '#s(future-event 1 1 start-work 1334778392134.673 #f #f))
|
||||
(indexed-fevent 38 '#s(future-event 1 1 block 1334778392134.676 #f #f))
|
||||
(indexed-fevent 39 '#s(future-event 1 1 suspend 1334778392134.677 #f #f))
|
||||
(indexed-fevent 40 '#s(future-event 1 1 end-work 1334778392134.677 #f #f))
|
||||
(indexed-fevent 41 '#s(future-event 1 0 touch-pause 1334778392134.704 #f #f))
|
||||
(indexed-fevent 42 '#s(future-event 1 0 touch-resume 1334778392134.704 #f #f))
|
||||
(indexed-fevent 43 '#s(future-event 1 0 block 1334778392134.727 * #f))
|
||||
(indexed-fevent 44 '#s(future-event 1 0 result 1334778392134.73 #f #f))
|
||||
(indexed-fevent 45 '#s(future-event 1 2 start-work 1334778392134.737 #f #f))
|
||||
(indexed-fevent 46 '#s(future-event 1 2 block 1334778392134.739 #f #f))
|
||||
(indexed-fevent 47 '#s(future-event 1 2 suspend 1334778392134.74 #f #f))
|
||||
(indexed-fevent 48 '#s(future-event 1 2 end-work 1334778392134.741 #f #f))
|
||||
(indexed-fevent 49 '#s(future-event 1 0 touch-pause 1334778392134.767 #f #f))
|
||||
(indexed-fevent 50 '#s(future-event 1 0 touch-resume 1334778392134.767 #f #f))
|
||||
(indexed-fevent 51 '#s(future-event 1 0 block 1334778392134.79 > #f))
|
||||
(indexed-fevent 52 '#s(future-event 1 0 result 1334778392134.793 #f #f))
|
||||
(indexed-fevent 53 '#s(future-event 1 1 start-work 1334778392134.799 #f #f))
|
||||
(indexed-fevent 54 '#s(future-event 1 1 block 1334778392134.801 #f #f))
|
||||
(indexed-fevent 55 '#s(future-event 1 1 suspend 1334778392134.802 #f #f))
|
||||
(indexed-fevent 56 '#s(future-event 1 1 end-work 1334778392134.803 #f #f))
|
||||
(indexed-fevent 57 '#s(future-event 1 0 touch-pause 1334778392134.832 #f #f))
|
||||
(indexed-fevent 58 '#s(future-event 1 0 touch-resume 1334778392134.832 #f #f))
|
||||
(indexed-fevent 59 '#s(future-event 1 0 block 1334778392134.854 * #f))
|
||||
(indexed-fevent 60 '#s(future-event 1 0 result 1334778392134.858 #f #f))
|
||||
(indexed-fevent 61 '#s(future-event 1 2 start-work 1334778392134.864 #f #f))
|
||||
(indexed-fevent 62 '#s(future-event 1 2 block 1334778392134.876 #f #f))
|
||||
(indexed-fevent 63 '#s(future-event 1 2 suspend 1334778392134.877 #f #f))
|
||||
(indexed-fevent 64 '#s(future-event 1 2 end-work 1334778392134.882 #f #f))
|
||||
(indexed-fevent 65 '#s(future-event 1 0 touch-pause 1334778392134.918 #f #f))
|
||||
(indexed-fevent 66 '#s(future-event 1 0 touch-resume 1334778392134.918 #f #f))
|
||||
(indexed-fevent 67 '#s(future-event 1 0 block 1334778392134.94 > #f))
|
||||
(indexed-fevent 68 '#s(future-event 1 0 result 1334778392134.943 #f #f))
|
||||
(indexed-fevent 69 '#s(future-event 1 1 start-work 1334778392134.949 #f #f))
|
||||
(indexed-fevent 70 '#s(future-event 1 1 block 1334778392134.952 #f #f))
|
||||
(indexed-fevent 71 '#s(future-event 1 1 suspend 1334778392134.953 #f #f))
|
||||
(indexed-fevent 72 '#s(future-event 1 1 end-work 1334778392134.96 #f #f))
|
||||
(indexed-fevent 73 '#s(future-event 1 0 touch-pause 1334778392134.991 #f #f))
|
||||
(indexed-fevent 74 '#s(future-event 1 0 touch-resume 1334778392134.991 #f #f))
|
||||
(indexed-fevent 75 '#s(future-event 1 0 block 1334778392135.013 * #f))
|
||||
(indexed-fevent 76 '#s(future-event 1 0 result 1334778392135.016 #f #f))
|
||||
(indexed-fevent 77 '#s(future-event 1 2 start-work 1334778392135.027 #f #f))
|
||||
(indexed-fevent 78 '#s(future-event 1 2 block 1334778392135.033 #f #f))
|
||||
(indexed-fevent 79 '#s(future-event 1 2 suspend 1334778392135.034 #f #f))
|
||||
(indexed-fevent 80 '#s(future-event 1 2 end-work 1334778392135.04 #f #f))
|
||||
(indexed-fevent 81 '#s(future-event 1 0 touch-pause 1334778392135.075 #f #f))
|
||||
(indexed-fevent 82 '#s(future-event 1 0 touch-resume 1334778392135.075 #f #f))
|
||||
(indexed-fevent 83 '#s(future-event 1 0 block 1334778392135.098 > #f))
|
||||
(indexed-fevent 84 '#s(future-event 1 0 result 1334778392135.101 #f #f))
|
||||
(indexed-fevent 85 '#s(future-event 1 1 start-work 1334778392135.107 #f #f))
|
||||
(indexed-fevent 86 '#s(future-event 1 1 block 1334778392135.117 #f #f))
|
||||
(indexed-fevent 87 '#s(future-event 1 1 suspend 1334778392135.118 #f #f))
|
||||
(indexed-fevent 88 '#s(future-event 1 1 end-work 1334778392135.123 #f #f))
|
||||
(indexed-fevent 89 '#s(future-event 1 0 touch-pause 1334778392135.159 #f #f))
|
||||
(indexed-fevent 90 '#s(future-event 1 0 touch-resume 1334778392135.159 #f #f))
|
||||
(indexed-fevent 91 '#s(future-event 1 0 block 1334778392135.181 * #f))
|
||||
(indexed-fevent 92 '#s(future-event 1 0 result 1334778392135.184 #f #f))
|
||||
(indexed-fevent 93 '#s(future-event 1 2 start-work 1334778392135.19 #f #f))
|
||||
(indexed-fevent 94 '#s(future-event 1 2 block 1334778392135.191 #f #f))
|
||||
(indexed-fevent 95 '#s(future-event 1 2 suspend 1334778392135.192 #f #f))
|
||||
(indexed-fevent 96 '#s(future-event 1 2 end-work 1334778392135.192 #f #f))
|
||||
(indexed-fevent 97 '#s(future-event 1 0 touch-pause 1334778392135.221 #f #f))
|
||||
(indexed-fevent 98 '#s(future-event 1 0 touch-resume 1334778392135.221 #f #f))
|
||||
(indexed-fevent 99 '#s(future-event 1 0 block 1334778392135.243 > #f))
|
||||
)))
|
||||
|
||||
@interaction-eval-show[
|
||||
#:eval future-eval
|
||||
(build-timeline-bmp-from-log bad-log
|
||||
#:max-width 600
|
||||
#:max-height 300)
|
||||
]
|
||||
|
||||
Each horizontal row represents an OS-level thread, and the colored
|
||||
dots represent important events in the execution of the program (they are
|
||||
color-coded to distinguish one event type from another). The upper-left blue
|
||||
dot in the timeline represents the future's creation. The future
|
||||
executes for a brief period (represented by a green bar in the second line) on thread
|
||||
1, and then pauses to allow the runtime thread to perform a future-unsafe operation.
|
||||
|
||||
In the Racket implementation, future-unsafe operations fall into one of two categories.
|
||||
A @deftech{blocking} operation halts the evaluation of the future, and will not allow
|
||||
it to continue until it is touched. After the operation completes within @racket[touch],
|
||||
the remainder of the future's work will be evaluated sequentially by the runtime
|
||||
thread. A @deftech{synchronized} operation also halts the future, but the runtime thread
|
||||
may perform the operation at any time and, once completed, the future may continue
|
||||
running in parallel. Memory allocation and JIT compilation are two common examples
|
||||
of synchronized operations.
|
||||
|
||||
In the timeline, we see an orange dot just to the right of the green bar on thread 1 --
|
||||
this dot represents a synchronized operation (memory allocation). The first orange
|
||||
dot on thread 0 shows that the runtime thread performed the allocation shortly after
|
||||
the future paused. A short time later, the future halts on a blocking operation
|
||||
(the first red dot) and must wait until the @racket[touch] for it to be evaluated
|
||||
(slightly after the 1049ms mark).
|
||||
|
||||
When you move your mouse over an event, the visualizer shows you
|
||||
detailed information about the event and draws arrows
|
||||
connecting all of the events in the corresponding future.
|
||||
This image shows those connections for our future.
|
||||
|
||||
@interaction-eval-show[
|
||||
#:eval future-eval
|
||||
(build-timeline-bmp-with-overlay bad-log
|
||||
6
|
||||
#:max-width 600
|
||||
#:max-height 300)
|
||||
]
|
||||
|
||||
The dotted orange line connects the first event in the future to
|
||||
the future that created it, and the purple lines connect adjacent
|
||||
events within the future.
|
||||
|
||||
The reason that we see no parallelism is that the @racket[<] and @racket[*] operations
|
||||
in the lower portion of the loop in @racket[mandelbrot] involve a mixture of
|
||||
floating-point and fixed (integer) values. Such mixtures typically trigger a slow
|
||||
path in execution, and the general slow path will usually be blocking.
|
||||
|
||||
Changing constants to be floating-points numbers in @racket[mandelbrot] addresses that
|
||||
first problem:
|
||||
|
||||
@racketblock[
|
||||
(define (mandelbrot iterations x y n)
|
||||
(let ([ci (- (/ (* 2.0 (->fl y)) (->fl n)) 1.0)]
|
||||
[cr (- (/ (* 2.0 (->fl x)) (->fl n)) 1.5)])
|
||||
....))
|
||||
(let ([ci (- (/ (* 2.0 y) n) 1.0)]
|
||||
[cr (- (/ (* 2.0 x) n) 1.5)])
|
||||
(let loop ([i 0] [zr 0.0] [zi 0.0])
|
||||
(if (> i iterations)
|
||||
i
|
||||
(let ([zrq (* zr zr)]
|
||||
[ziq (* zi zi)])
|
||||
(cond
|
||||
[(> (+ zrq ziq) 4.0) i]
|
||||
[else (loop (add1 i)
|
||||
(+ (- zrq ziq) cr)
|
||||
(+ (* 2.0 zr zi) ci))]))))))
|
||||
]
|
||||
|
||||
With that change, @racket[mandelbrot] computations can run in
|
||||
parallel. Nevertheless, performance still does not improve. The
|
||||
problem is that most every arithmetic operation in this example
|
||||
produces an inexact number whose storage must be allocated. Especially
|
||||
frequent allocation triggers communication between parallel tasks that
|
||||
defeats any performance improvement.
|
||||
With that change, @racket[mandelbrot] computations can run in
|
||||
parallel. Nevertheless, we still see a special type of
|
||||
slow-path operation limiting our parallelism (orange dots):
|
||||
|
||||
@interaction-eval[
|
||||
#:eval future-eval
|
||||
(define better-log
|
||||
(list (indexed-fevent 0 '#s(future-event #f 0 create 1334779296782.22 #f 2))
|
||||
(indexed-fevent 1 '#s(future-event 2 2 start-work 1334779296782.265 #f #f))
|
||||
(indexed-fevent 2 '#s(future-event 2 2 sync 1334779296782.378 #f #f))
|
||||
(indexed-fevent 3 '#s(future-event 2 0 sync 1334779296795.582 [allocate memory] #f))
|
||||
(indexed-fevent 4 '#s(future-event 2 0 result 1334779296795.587 #f #f))
|
||||
(indexed-fevent 5 '#s(future-event 2 2 result 1334779296795.6 #f #f))
|
||||
(indexed-fevent 6 '#s(future-event 2 2 sync 1334779296795.689 #f #f))
|
||||
(indexed-fevent 7 '#s(future-event 2 0 sync 1334779296795.807 [allocate memory] #f))
|
||||
(indexed-fevent 8 '#s(future-event 2 0 result 1334779296795.812 #f #f))
|
||||
(indexed-fevent 9 '#s(future-event 2 2 result 1334779296795.818 #f #f))
|
||||
(indexed-fevent 10 '#s(future-event 2 2 sync 1334779296795.827 #f #f))
|
||||
(indexed-fevent 11 '#s(future-event 2 0 sync 1334779296806.627 [allocate memory] #f))
|
||||
(indexed-fevent 12 '#s(future-event 2 0 result 1334779296806.635 #f #f))
|
||||
(indexed-fevent 13 '#s(future-event 2 2 result 1334779296806.646 #f #f))
|
||||
(indexed-fevent 14 '#s(future-event 2 2 sync 1334779296806.879 #f #f))
|
||||
(indexed-fevent 15 '#s(future-event 2 0 sync 1334779296806.994 [allocate memory] #f))
|
||||
(indexed-fevent 16 '#s(future-event 2 0 result 1334779296806.999 #f #f))
|
||||
(indexed-fevent 17 '#s(future-event 2 2 result 1334779296807.007 #f #f))
|
||||
(indexed-fevent 18 '#s(future-event 2 2 sync 1334779296807.023 #f #f))
|
||||
(indexed-fevent 19 '#s(future-event 2 0 sync 1334779296814.198 [allocate memory] #f))
|
||||
(indexed-fevent 20 '#s(future-event 2 0 result 1334779296814.206 #f #f))
|
||||
(indexed-fevent 21 '#s(future-event 2 2 result 1334779296814.221 #f #f))
|
||||
(indexed-fevent 22 '#s(future-event 2 2 sync 1334779296814.29 #f #f))
|
||||
(indexed-fevent 23 '#s(future-event 2 0 sync 1334779296820.796 [allocate memory] #f))
|
||||
(indexed-fevent 24 '#s(future-event 2 0 result 1334779296820.81 #f #f))
|
||||
(indexed-fevent 25 '#s(future-event 2 2 result 1334779296820.835 #f #f))
|
||||
(indexed-fevent 26 '#s(future-event 2 2 sync 1334779296821.089 #f #f))
|
||||
(indexed-fevent 27 '#s(future-event 2 0 sync 1334779296825.217 [allocate memory] #f))
|
||||
(indexed-fevent 28 '#s(future-event 2 0 result 1334779296825.226 #f #f))
|
||||
(indexed-fevent 29 '#s(future-event 2 2 result 1334779296825.242 #f #f))
|
||||
(indexed-fevent 30 '#s(future-event 2 2 sync 1334779296825.305 #f #f))
|
||||
(indexed-fevent 31 '#s(future-event 2 0 sync 1334779296832.541 [allocate memory] #f))
|
||||
(indexed-fevent 32 '#s(future-event 2 0 result 1334779296832.549 #f #f))
|
||||
(indexed-fevent 33 '#s(future-event 2 2 result 1334779296832.562 #f #f))
|
||||
(indexed-fevent 34 '#s(future-event 2 2 sync 1334779296832.667 #f #f))
|
||||
(indexed-fevent 35 '#s(future-event 2 0 sync 1334779296836.269 [allocate memory] #f))
|
||||
(indexed-fevent 36 '#s(future-event 2 0 result 1334779296836.278 #f #f))
|
||||
(indexed-fevent 37 '#s(future-event 2 2 result 1334779296836.326 #f #f))
|
||||
(indexed-fevent 38 '#s(future-event 2 2 sync 1334779296836.396 #f #f))
|
||||
(indexed-fevent 39 '#s(future-event 2 0 sync 1334779296843.481 [allocate memory] #f))
|
||||
(indexed-fevent 40 '#s(future-event 2 0 result 1334779296843.49 #f #f))
|
||||
(indexed-fevent 41 '#s(future-event 2 2 result 1334779296843.501 #f #f))
|
||||
(indexed-fevent 42 '#s(future-event 2 2 sync 1334779296843.807 #f #f))
|
||||
(indexed-fevent 43 '#s(future-event 2 0 sync 1334779296847.291 [allocate memory] #f))
|
||||
(indexed-fevent 44 '#s(future-event 2 0 result 1334779296847.3 #f #f))
|
||||
(indexed-fevent 45 '#s(future-event 2 2 result 1334779296847.312 #f #f))
|
||||
(indexed-fevent 46 '#s(future-event 2 2 sync 1334779296847.375 #f #f))
|
||||
(indexed-fevent 47 '#s(future-event 2 0 sync 1334779296854.487 [allocate memory] #f))
|
||||
(indexed-fevent 48 '#s(future-event 2 0 result 1334779296854.495 #f #f))
|
||||
(indexed-fevent 49 '#s(future-event 2 2 result 1334779296854.507 #f #f))
|
||||
(indexed-fevent 50 '#s(future-event 2 2 sync 1334779296854.656 #f #f))
|
||||
(indexed-fevent 51 '#s(future-event 2 0 sync 1334779296857.374 [allocate memory] #f))
|
||||
(indexed-fevent 52 '#s(future-event 2 0 result 1334779296857.383 #f #f))
|
||||
(indexed-fevent 53 '#s(future-event 2 2 result 1334779296857.421 #f #f))
|
||||
(indexed-fevent 54 '#s(future-event 2 2 sync 1334779296857.488 #f #f))
|
||||
(indexed-fevent 55 '#s(future-event 2 0 sync 1334779296869.919 [allocate memory] #f))
|
||||
(indexed-fevent 56 '#s(future-event 2 0 result 1334779296869.947 #f #f))
|
||||
(indexed-fevent 57 '#s(future-event 2 2 result 1334779296869.981 #f #f))
|
||||
(indexed-fevent 58 '#s(future-event 2 2 sync 1334779296870.32 #f #f))
|
||||
(indexed-fevent 59 '#s(future-event 2 0 sync 1334779296879.438 [allocate memory] #f))
|
||||
(indexed-fevent 60 '#s(future-event 2 0 result 1334779296879.446 #f #f))
|
||||
(indexed-fevent 61 '#s(future-event 2 2 result 1334779296879.463 #f #f))
|
||||
(indexed-fevent 62 '#s(future-event 2 2 sync 1334779296879.526 #f #f))
|
||||
(indexed-fevent 63 '#s(future-event 2 0 sync 1334779296882.928 [allocate memory] #f))
|
||||
(indexed-fevent 64 '#s(future-event 2 0 result 1334779296882.935 #f #f))
|
||||
(indexed-fevent 65 '#s(future-event 2 2 result 1334779296882.944 #f #f))
|
||||
(indexed-fevent 66 '#s(future-event 2 2 sync 1334779296883.311 #f #f))
|
||||
(indexed-fevent 67 '#s(future-event 2 0 sync 1334779296890.471 [allocate memory] #f))
|
||||
(indexed-fevent 68 '#s(future-event 2 0 result 1334779296890.479 #f #f))
|
||||
(indexed-fevent 69 '#s(future-event 2 2 result 1334779296890.517 #f #f))
|
||||
(indexed-fevent 70 '#s(future-event 2 2 sync 1334779296890.581 #f #f))
|
||||
(indexed-fevent 71 '#s(future-event 2 0 sync 1334779296894.362 [allocate memory] #f))
|
||||
(indexed-fevent 72 '#s(future-event 2 0 result 1334779296894.369 #f #f))
|
||||
(indexed-fevent 73 '#s(future-event 2 2 result 1334779296894.382 #f #f))
|
||||
(indexed-fevent 74 '#s(future-event 2 2 sync 1334779296894.769 #f #f))
|
||||
(indexed-fevent 75 '#s(future-event 2 0 sync 1334779296901.501 [allocate memory] #f))
|
||||
(indexed-fevent 76 '#s(future-event 2 0 result 1334779296901.51 #f #f))
|
||||
(indexed-fevent 77 '#s(future-event 2 2 result 1334779296901.556 #f #f))
|
||||
(indexed-fevent 78 '#s(future-event 2 2 sync 1334779296901.62 #f #f))
|
||||
(indexed-fevent 79 '#s(future-event 2 0 sync 1334779296905.428 [allocate memory] #f))
|
||||
(indexed-fevent 80 '#s(future-event 2 0 result 1334779296905.434 #f #f))
|
||||
(indexed-fevent 81 '#s(future-event 2 2 result 1334779296905.447 #f #f))
|
||||
(indexed-fevent 82 '#s(future-event 2 2 sync 1334779296905.743 #f #f))
|
||||
(indexed-fevent 83 '#s(future-event 2 0 sync 1334779296912.538 [allocate memory] #f))
|
||||
(indexed-fevent 84 '#s(future-event 2 0 result 1334779296912.547 #f #f))
|
||||
(indexed-fevent 85 '#s(future-event 2 2 result 1334779296912.564 #f #f))
|
||||
(indexed-fevent 86 '#s(future-event 2 2 sync 1334779296912.625 #f #f))
|
||||
(indexed-fevent 87 '#s(future-event 2 0 sync 1334779296916.094 [allocate memory] #f))
|
||||
(indexed-fevent 88 '#s(future-event 2 0 result 1334779296916.1 #f #f))
|
||||
(indexed-fevent 89 '#s(future-event 2 2 result 1334779296916.108 #f #f))
|
||||
(indexed-fevent 90 '#s(future-event 2 2 sync 1334779296916.243 #f #f))
|
||||
(indexed-fevent 91 '#s(future-event 2 0 sync 1334779296927.233 [allocate memory] #f))
|
||||
(indexed-fevent 92 '#s(future-event 2 0 result 1334779296927.242 #f #f))
|
||||
(indexed-fevent 93 '#s(future-event 2 2 result 1334779296927.262 #f #f))
|
||||
(indexed-fevent 94 '#s(future-event 2 2 sync 1334779296927.59 #f #f))
|
||||
(indexed-fevent 95 '#s(future-event 2 0 sync 1334779296934.603 [allocate memory] #f))
|
||||
(indexed-fevent 96 '#s(future-event 2 0 result 1334779296934.612 #f #f))
|
||||
(indexed-fevent 97 '#s(future-event 2 2 result 1334779296934.655 #f #f))
|
||||
(indexed-fevent 98 '#s(future-event 2 2 sync 1334779296934.72 #f #f))
|
||||
(indexed-fevent 99 '#s(future-event 2 0 sync 1334779296938.773 [allocate memory] #f))
|
||||
))
|
||||
]
|
||||
|
||||
@interaction-eval-show[
|
||||
#:eval future-eval
|
||||
(build-timeline-bmp-from-log better-log #:max-width 600 #:max-height 300)
|
||||
]
|
||||
|
||||
The problem is that most every arithmetic operation in this example
|
||||
produces an inexact number whose storage must be allocated. While some allocation
|
||||
can safely be performed exclusively without the aid of the runtime thread, especially
|
||||
frequent allocation requires synchronized operations which defeat any performance
|
||||
improvement.
|
||||
|
||||
By using @tech{flonum}-specific operations (see
|
||||
@secref["fixnums+flonums"]), we can re-write @racket[mandelbot] to use
|
||||
@secref["fixnums+flonums"]), we can re-write @racket[mandelbrot] to use
|
||||
much less allocation:
|
||||
|
||||
@interaction-eval[
|
||||
#:eval future-eval
|
||||
(define good-log
|
||||
(list (indexed-fevent 0 '#s(future-event #f 0 create 1334778395768.733 #f 3))
|
||||
(indexed-fevent 1 '#s(future-event 3 2 start-work 1334778395768.771 #f #f))
|
||||
(indexed-fevent 2 '#s(future-event 3 2 complete 1334778395864.648 #f #f))
|
||||
(indexed-fevent 3 '#s(future-event 3 2 end-work 1334778395864.652 #f #f))
|
||||
))
|
||||
]
|
||||
|
||||
@racketblock[
|
||||
(define (mandelbrot iterations x y n)
|
||||
(let ([ci (fl- (fl/ (* 2.0 (->fl y)) (->fl n)) 1.0)]
|
||||
|
@ -145,42 +450,23 @@ much less allocation:
|
|||
This conversion can speed @racket[mandelbrot] by a factor of 8, even
|
||||
in sequential mode, but avoiding allocation also allows
|
||||
@racket[mandelbrot] to run usefully faster in parallel.
|
||||
Executing this program yields the following in the visualizer:
|
||||
|
||||
@interaction-eval-show[
|
||||
#:eval future-eval
|
||||
(build-timeline-bmp-from-log good-log
|
||||
#:max-width 600
|
||||
#:max-height 300)
|
||||
]
|
||||
|
||||
Notice that only one green bar is shown here because one of the
|
||||
mandelbrot computations is not being evaluated by a future (on
|
||||
the runtime thread).
|
||||
|
||||
As a general guideline, any operation that is inlined by the
|
||||
@tech{JIT} compiler runs safely in parallel, while other operations
|
||||
that are not inlined (including all operations if the JIT compiler is
|
||||
disabled) are considered unsafe. The @exec{mzc} decompiler tool
|
||||
disabled) are considered unsafe. The @exec{raco decompile} tool
|
||||
annotates operations that can be inlined by the compiler (see
|
||||
@secref[#:doc '(lib "scribblings/raco/raco.scrbl") "decompile"]), so the
|
||||
decompiler can be used to help predict parallel performance.
|
||||
|
||||
To more directly report what is happening in a program that uses
|
||||
@racket[future] and @racket[touch], operations are logged when they
|
||||
suspend a computation or synchronize with the main computation. For
|
||||
example, running the original @racket[mandelbrot] in a future produces
|
||||
the following output in the @racket['debug] log level:
|
||||
|
||||
@margin-note{To see @racket['debug] logging output on stderr, set the
|
||||
@envvar{PLTSTDERR} environment variable to @tt{debug} or start
|
||||
@exec{racket} with @Flag{W} @tt{debug}.}
|
||||
|
||||
@verbatim[#:indent 2]|{
|
||||
future 1, process 1: BLOCKING on process 0; time: ....
|
||||
....
|
||||
future 1, process 0: HANDLING: *; time: ....
|
||||
}|
|
||||
|
||||
The messages indicate which internal future-running task became
|
||||
blocked on an unsafe operation, the time it blocked (in terms of
|
||||
@racket[current-inexact-miliseconds]), and the operation that caused
|
||||
the computation it to block.
|
||||
|
||||
The first revision to @racket[mandelbrot] avoids suspending at
|
||||
@racket[*], but produces many log entries of the form
|
||||
|
||||
@verbatim[#:indent 2]|{
|
||||
future 1, process 0: synchronizing: [allocate memory]; time: ....
|
||||
}|
|
||||
|
||||
The @tt{[allocate memory]} part of the message indicates that
|
||||
synchronization was needed for memory allocation.
|
||||
|
|
BIN
collects/scribblings/guide/mand-bad-hover.png
Normal file
BIN
collects/scribblings/guide/mand-bad-hover.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
collects/scribblings/guide/mand-bad.png
Normal file
BIN
collects/scribblings/guide/mand-bad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
collects/scribblings/guide/mand-good.png
Normal file
BIN
collects/scribblings/guide/mand-good.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
collects/scribblings/guide/vis-main.png
Normal file
BIN
collects/scribblings/guide/vis-main.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -17,5 +17,6 @@ support for parallelism to improve performance.
|
|||
@include-section["sync.scrbl"]
|
||||
@include-section["thread-local.scrbl"]
|
||||
@include-section["futures.scrbl"]
|
||||
@include-section["futures-visualizer.scrbl"]
|
||||
@include-section["places.scrbl"]
|
||||
@include-section["distributed.scrbl"]
|
||||
|
|
86
collects/scribblings/reference/futures-visualizer.scrbl
Normal file
86
collects/scribblings/reference/futures-visualizer.scrbl
Normal file
|
@ -0,0 +1,86 @@
|
|||
#lang scribble/doc
|
||||
@(require "mz.rkt" #;(for-label racket/future/visualizer))
|
||||
|
||||
@title[#:tag "futures-visualizer"]{Futures Visualizer}
|
||||
|
||||
@guideintro["effective-futures"]{the future visualizer}
|
||||
|
||||
@defmodule[racket/future/visualizer]
|
||||
|
||||
The @deftech{futures visualizer} is a graphical profiling tool
|
||||
for parallel programs written using @racket[future]. The tool
|
||||
shows a timeline of a program's execution including all future-related
|
||||
events, as well as the overall amount of processor utilization
|
||||
at any point during the program's lifetime.
|
||||
|
||||
@deftogether[(
|
||||
@defproc[(start-performance-tracking!) void?]
|
||||
@defproc[(show-visualizer) void?]
|
||||
)]{
|
||||
The @racket[start-performance-tracking!] procedure enables the collection
|
||||
of data required by the visualizer. This function should be called immediately
|
||||
prior to executing code the programmer wishes to profile.
|
||||
|
||||
The @racket[show-visualizer] procedure displays the profiler window.
|
||||
|
||||
A typical program using profiling might look like the following:
|
||||
|
||||
@racketblock[
|
||||
(require racket/future
|
||||
racket/future/visualizer)
|
||||
|
||||
(start-performance-tracking!)
|
||||
(let ([f (future (lambda () ...))])
|
||||
...
|
||||
(touch f))
|
||||
|
||||
(show-visualizer)
|
||||
]
|
||||
}
|
||||
|
||||
@section[#:tag "future-visualizer-timeline"]{Execution Timeline}
|
||||
|
||||
The @deftech{execution timeline}, shown in the top left-hand corner of the
|
||||
profiler window, displays a history of the program
|
||||
and all events associated with its futures, with OS-level threads
|
||||
or @deftech{processes} organized along the y-axis and time increasing along
|
||||
the x-axis. A coloring convention is used to distinguish between
|
||||
different types of events (see @secref["future-logging"] for a full
|
||||
description of these event types):
|
||||
|
||||
@itemlist[
|
||||
@item{Blue dot: @racket['create]}
|
||||
|
||||
@item{Green bar: @racket['start-work], @racket['start-0-work]}
|
||||
|
||||
@item{Orange dot: @racket['sync]}
|
||||
|
||||
@item{Red dot: @racket['block], @racket['touch]}
|
||||
|
||||
@item{White dot: @racket['result], @racket['end-work]}
|
||||
|
||||
@item{Green dot: @racket['touch-pause], @racket['touch-resume]}
|
||||
]
|
||||
|
||||
Mousing over any event connects it via purple lines to the sequence
|
||||
of events for its future. Additionally, orange dotted lines
|
||||
with arrowheads may be shown to indicate operations performed from
|
||||
one future to another (e.g. @racket['create] or @racket['touch] actions).
|
||||
To view details about two events simultaneously, a selection
|
||||
can be tacked by clicking the mouse.
|
||||
|
||||
The timeline displays vertical lines at 100-microsecond intervals. Note that
|
||||
though the time interval is fixed, the pixel distance between lines varies
|
||||
based on the event density for any given time range to prevent overlapping
|
||||
event circles.
|
||||
|
||||
@section[#:tag "future-visualizer-tree"]{Future Creation Tree}
|
||||
|
||||
The @deftech{creation tree} shows a tree with a single node per
|
||||
future created by the program. This display can be particularly useful
|
||||
for programs which spawn futures in nested fashion (futures within futures).
|
||||
For any given future node, the children
|
||||
of that node represent futures which were created by that future (within
|
||||
the scope of its thunk). For all programs, the root of the tree
|
||||
is a special node representing the main computation thread (the runtime thread),
|
||||
and is denoted @deftech{RTT}.
|
|
@ -183,16 +183,16 @@ Racket futures use logging (see @secref["logging"]) extensively to
|
|||
report information about how futures are evaluated. Logging output is
|
||||
useful for debugging the performance of programs that use futures.
|
||||
|
||||
Though textual log output can be viewed directly, it is much
|
||||
easier to use the graphical profiler tool provided by
|
||||
@racketmodname[racket/future/visualizer].
|
||||
|
||||
In addition to its string message, each event logged for a future has
|
||||
a data value that is an instance of a @racket[future-event]
|
||||
@tech{prefab} structure:
|
||||
|
||||
@racketblock[
|
||||
(define-struct future-event (future-id
|
||||
proc-id
|
||||
action
|
||||
time
|
||||
unsafe-op-name)
|
||||
(define-struct future-event (future-id proc-id action time unsafe-op-name target-fid)
|
||||
#:prefab)
|
||||
]
|
||||
|
||||
|
@ -281,7 +281,13 @@ In process 0, some event pairs can be nested within other event pairs:
|
|||
An @racket[block] in process 0 is generated when an unsafe operation
|
||||
is handled. This type of event will contain a symbol in the
|
||||
@racket[unsafe-op-name] field that is the name of the operation. In all
|
||||
other cases, this field contains @racket[#f].}
|
||||
other cases, this field contains @racket[#f].
|
||||
|
||||
The @racket[target-fid] field contains an exact integer value in certain
|
||||
cases where the @racket[action] occurs in one future but is being
|
||||
performed on another (e.g. @racket['create] or @racket['touch]). In such
|
||||
cases, the integer value is the identifier of the future on which the action
|
||||
is being performed. In all other cases, this field contains @racket[#f].
|
||||
|
||||
@; ----------------------------------------------------------------------
|
||||
|
||||
|
|
105
collects/tests/future/bad-trace1.rkt
Normal file
105
collects/tests/future/bad-trace1.rkt
Normal file
|
@ -0,0 +1,105 @@
|
|||
#lang racket
|
||||
(require racket/future/private/visualizer-data)
|
||||
(provide BAD-TRACE-1)
|
||||
|
||||
(define BAD-TRACE-1 (list (indexed-fevent 0 '#s(future-event #f 0 create 1334779294212.415 #f 1))
|
||||
(indexed-fevent 1 '#s(future-event 1 1 start-work 1334779294212.495 #f #f))
|
||||
(indexed-fevent 2 '#s(future-event 1 1 sync 1334779294212.501 #f #f))
|
||||
(indexed-fevent 3 (future-event 1 0 'sync 1334779294221.128 'allocate_memory #f))
|
||||
(indexed-fevent 4 '#s(future-event 1 0 result 1334779294221.138 #f #f))
|
||||
(indexed-fevent 5 '#s(future-event 1 1 result 1334779294221.15 #f #f))
|
||||
(indexed-fevent 6 '#s(future-event 1 1 block 1334779294221.158 #f #f))
|
||||
(indexed-fevent 7 '#s(future-event 1 1 suspend 1334779294221.164 #f #f))
|
||||
(indexed-fevent 8 '#s(future-event 1 1 end-work 1334779294221.165 #f #f))
|
||||
(indexed-fevent 9 '#s(future-event 1 0 block 1334779295356.267 > #f))
|
||||
(indexed-fevent 10 '#s(future-event 1 0 result 1334779295356.293 #f #f))
|
||||
(indexed-fevent 11 '#s(future-event 1 1 start-work 1334779295356.316 #f #f))
|
||||
(indexed-fevent 12 '#s(future-event 1 1 sync 1334779295356.448 #f #f))
|
||||
(indexed-fevent 13 '#s(future-event 1 0 touch-pause 1334779295356.451 #f #f))
|
||||
(indexed-fevent 14 '#s(future-event 1 0 touch-resume 1334779295356.472 #f #f))
|
||||
(indexed-fevent 15 (future-event 1 0 'block 1334779295356.936 'allocate_memory #f))
|
||||
(indexed-fevent 16 '#s(future-event 1 0 result 1334779295356.959 #f #f))
|
||||
(indexed-fevent 17 '#s(future-event 1 1 result 1334779295356.97 #f #f))
|
||||
(indexed-fevent 18 '#s(future-event 1 1 block 1334779295356.988 #f #f))
|
||||
(indexed-fevent 19 '#s(future-event 1 1 suspend 1334779295356.99 #f #f))
|
||||
(indexed-fevent 20 '#s(future-event 1 1 end-work 1334779295357.0 #f #f))
|
||||
(indexed-fevent 21 '#s(future-event 1 0 touch-pause 1334779295357.066 #f #f))
|
||||
(indexed-fevent 22 '#s(future-event 1 0 touch-resume 1334779295357.066 #f #f))
|
||||
(indexed-fevent 23 '#s(future-event 1 0 block 1334779295357.104 * #f))
|
||||
(indexed-fevent 24 '#s(future-event 1 0 result 1334779295357.11 #f #f))
|
||||
(indexed-fevent 25 '#s(future-event 1 1 start-work 1334779295357.119 #f #f))
|
||||
(indexed-fevent 26 '#s(future-event 1 1 block 1334779295357.122 #f #f))
|
||||
(indexed-fevent 27 '#s(future-event 1 1 suspend 1334779295357.123 #f #f))
|
||||
(indexed-fevent 28 '#s(future-event 1 1 end-work 1334779295357.124 #f #f))
|
||||
(indexed-fevent 29 '#s(future-event 1 0 touch-pause 1334779295357.174 #f #f))
|
||||
(indexed-fevent 30 '#s(future-event 1 0 touch-resume 1334779295357.174 #f #f))
|
||||
(indexed-fevent 31 '#s(future-event 1 0 block 1334779295357.21 > #f))
|
||||
(indexed-fevent 32 '#s(future-event 1 0 result 1334779295357.216 #f #f))
|
||||
(indexed-fevent 33 '#s(future-event 1 1 start-work 1334779295357.224 #f #f))
|
||||
(indexed-fevent 34 '#s(future-event 1 1 block 1334779295357.226 #f #f))
|
||||
(indexed-fevent 35 '#s(future-event 1 1 suspend 1334779295357.227 #f #f))
|
||||
(indexed-fevent 36 '#s(future-event 1 1 end-work 1334779295357.228 #f #f))
|
||||
(indexed-fevent 37 '#s(future-event 1 0 touch-pause 1334779295357.278 #f #f))
|
||||
(indexed-fevent 38 '#s(future-event 1 0 touch-resume 1334779295357.279 #f #f))
|
||||
(indexed-fevent 39 '#s(future-event 1 0 block 1334779295357.315 * #f))
|
||||
(indexed-fevent 40 '#s(future-event 1 0 result 1334779295357.32 #f #f))
|
||||
(indexed-fevent 41 '#s(future-event 1 1 start-work 1334779295357.328 #f #f))
|
||||
(indexed-fevent 42 '#s(future-event 1 1 block 1334779295357.337 #f #f))
|
||||
(indexed-fevent 43 '#s(future-event 1 1 suspend 1334779295357.338 #f #f))
|
||||
(indexed-fevent 44 '#s(future-event 1 1 end-work 1334779295357.347 #f #f))
|
||||
(indexed-fevent 45 '#s(future-event 1 0 touch-pause 1334779295357.407 #f #f))
|
||||
(indexed-fevent 46 '#s(future-event 1 0 touch-resume 1334779295357.408 #f #f))
|
||||
(indexed-fevent 47 '#s(future-event 1 0 block 1334779295357.445 > #f))
|
||||
(indexed-fevent 48 '#s(future-event 1 0 result 1334779295357.45 #f #f))
|
||||
(indexed-fevent 49 '#s(future-event 1 1 start-work 1334779295357.458 #f #f))
|
||||
(indexed-fevent 50 '#s(future-event 1 1 block 1334779295357.46 #f #f))
|
||||
(indexed-fevent 51 '#s(future-event 1 1 suspend 1334779295357.461 #f #f))
|
||||
(indexed-fevent 52 '#s(future-event 1 1 end-work 1334779295357.477 #f #f))
|
||||
(indexed-fevent 53 '#s(future-event 1 0 touch-pause 1334779295357.536 #f #f))
|
||||
(indexed-fevent 54 '#s(future-event 1 0 touch-resume 1334779295357.537 #f #f))
|
||||
(indexed-fevent 55 '#s(future-event 1 0 block 1334779295357.573 * #f))
|
||||
(indexed-fevent 56 '#s(future-event 1 0 result 1334779295357.579 #f #f))
|
||||
(indexed-fevent 57 '#s(future-event 1 1 start-work 1334779295357.587 #f #f))
|
||||
(indexed-fevent 58 '#s(future-event 1 1 block 1334779295357.595 #f #f))
|
||||
(indexed-fevent 59 '#s(future-event 1 1 suspend 1334779295357.596 #f #f))
|
||||
(indexed-fevent 60 '#s(future-event 1 1 end-work 1334779295357.597 #f #f))
|
||||
(indexed-fevent 61 '#s(future-event 1 0 touch-pause 1334779295357.644 #f #f))
|
||||
(indexed-fevent 62 '#s(future-event 1 0 touch-resume 1334779295357.645 #f #f))
|
||||
(indexed-fevent 63 '#s(future-event 1 0 block 1334779295357.68 > #f))
|
||||
(indexed-fevent 64 '#s(future-event 1 0 result 1334779295357.685 #f #f))
|
||||
(indexed-fevent 65 '#s(future-event 1 1 start-work 1334779295357.693 #f #f))
|
||||
(indexed-fevent 66 '#s(future-event 1 1 block 1334779295357.695 #f #f))
|
||||
(indexed-fevent 67 '#s(future-event 1 1 suspend 1334779295357.697 #f #f))
|
||||
(indexed-fevent 68 '#s(future-event 1 1 end-work 1334779295357.712 #f #f))
|
||||
(indexed-fevent 69 '#s(future-event 1 0 touch-pause 1334779295357.771 #f #f))
|
||||
(indexed-fevent 70 '#s(future-event 1 0 touch-resume 1334779295357.771 #f #f))
|
||||
(indexed-fevent 71 '#s(future-event 1 0 block 1334779295357.807 * #f))
|
||||
(indexed-fevent 72 '#s(future-event 1 0 result 1334779295357.813 #f #f))
|
||||
(indexed-fevent 73 '#s(future-event 1 1 start-work 1334779295357.821 #f #f))
|
||||
(indexed-fevent 74 '#s(future-event 1 1 block 1334779295357.823 #f #f))
|
||||
(indexed-fevent 75 '#s(future-event 1 1 suspend 1334779295357.824 #f #f))
|
||||
(indexed-fevent 76 '#s(future-event 1 1 end-work 1334779295357.84 #f #f))
|
||||
(indexed-fevent 77 '#s(future-event 1 0 touch-pause 1334779295357.899 #f #f))
|
||||
(indexed-fevent 78 '#s(future-event 1 0 touch-resume 1334779295357.899 #f #f))
|
||||
(indexed-fevent 79 '#s(future-event 1 0 block 1334779295357.936 > #f))
|
||||
(indexed-fevent 80 '#s(future-event 1 0 result 1334779295357.941 #f #f))
|
||||
(indexed-fevent 81 '#s(future-event 1 1 start-work 1334779295357.949 #f #f))
|
||||
(indexed-fevent 82 '#s(future-event 1 1 block 1334779295357.957 #f #f))
|
||||
(indexed-fevent 83 '#s(future-event 1 1 suspend 1334779295357.958 #f #f))
|
||||
(indexed-fevent 84 '#s(future-event 1 1 end-work 1334779295357.971 #f #f))
|
||||
(indexed-fevent 85 '#s(future-event 1 0 touch-pause 1334779295358.03 #f #f))
|
||||
(indexed-fevent 86 '#s(future-event 1 0 touch-resume 1334779295358.03 #f #f))
|
||||
(indexed-fevent 87 '#s(future-event 1 0 block 1334779295358.067 * #f))
|
||||
(indexed-fevent 88 '#s(future-event 1 0 result 1334779295358.072 #f #f))
|
||||
(indexed-fevent 89 '#s(future-event 1 1 start-work 1334779295358.08 #f #f))
|
||||
(indexed-fevent 90 '#s(future-event 1 1 block 1334779295358.088 #f #f))
|
||||
(indexed-fevent 91 '#s(future-event 1 1 suspend 1334779295358.09 #f #f))
|
||||
(indexed-fevent 92 '#s(future-event 1 1 end-work 1334779295358.099 #f #f))
|
||||
(indexed-fevent 93 '#s(future-event 1 0 touch-pause 1334779295358.15 #f #f))
|
||||
(indexed-fevent 94 '#s(future-event 1 0 touch-resume 1334779295358.15 #f #f))
|
||||
(indexed-fevent 95 '#s(future-event 1 0 block 1334779295358.187 > #f))
|
||||
(indexed-fevent 96 '#s(future-event 1 0 result 1334779295358.192 #f #f))
|
||||
(indexed-fevent 97 '#s(future-event 1 1 start-work 1334779295358.2 #f #f))
|
||||
(indexed-fevent 98 '#s(future-event 1 1 block 1334779295358.209 #f #f))
|
||||
(indexed-fevent 99 '#s(future-event 1 1 suspend 1334779295358.21 #f #f))
|
||||
))
|
|
@ -15,7 +15,7 @@ We should also test deep continuations.
|
|||
|#
|
||||
|
||||
;Tests specific to would-be-future
|
||||
(define-struct future-event (future-id process-id what time prim-name)
|
||||
(define-struct future-event (future-id process-id what time prim-name target-fid)
|
||||
#:prefab)
|
||||
|
||||
(define (get-events-of-type type log)
|
||||
|
|
273
collects/tests/future/visualizer.rkt
Normal file
273
collects/tests/future/visualizer.rkt
Normal file
|
@ -0,0 +1,273 @@
|
|||
#lang racket/base
|
||||
(require rackunit
|
||||
racket/vector
|
||||
racket/future/private/visualizer-drawing
|
||||
racket/future/private/visualizer-data
|
||||
racket/future/private/display
|
||||
"bad-trace1.rkt")
|
||||
|
||||
(define (compile-trace-data logs)
|
||||
(define tr (build-trace logs))
|
||||
(define-values (finfo segs) (calc-segments tr))
|
||||
(values tr
|
||||
finfo
|
||||
segs
|
||||
(frame-info-timeline-ticks finfo)))
|
||||
|
||||
;Display tests
|
||||
(let ([vr (viewable-region 3 3 500 500)])
|
||||
(for ([i (in-range 4 503)])
|
||||
(check-true (in-viewable-region-horiz vr i)
|
||||
(format "~a should be in ~a"
|
||||
i
|
||||
vr)))
|
||||
(for ([i (in-range 0 2)])
|
||||
(check-false (in-viewable-region-horiz vr i)
|
||||
(format "~a should not be in ~a"
|
||||
i
|
||||
vr))
|
||||
(for ([i (in-range 504 1000)])
|
||||
(check-false (in-viewable-region-horiz vr i)
|
||||
(format "~a should not be in ~a"
|
||||
i
|
||||
vr)))))
|
||||
|
||||
(let ([vr (viewable-region 0 0 732 685)])
|
||||
(check-true (in-viewable-region-horiz vr 10))
|
||||
(check-true (in-viewable-region-horiz vr 63.0))
|
||||
(check-true (in-viewable-region-horiz vr 116.0))
|
||||
(check-true (in-viewable-region-horiz vr 169.0))
|
||||
(check-true (in-viewable-region-horiz vr 222)))
|
||||
|
||||
(let ([vr (viewable-region 0 0 732 685)]
|
||||
[ticks (list (timeline-tick 222.0 #f 0.4999999999999982)
|
||||
(timeline-tick 169.0 #f 0.3999999999999986)
|
||||
(timeline-tick 116.0 #f 0.29999999999999893)
|
||||
(timeline-tick 63.0 #f 0.1999999999999993)
|
||||
(timeline-tick 10 #f 0.09999999999999964))])
|
||||
(define in-vr (filter (λ (t)
|
||||
(in-viewable-region-horiz vr (timeline-tick-x t)))
|
||||
ticks))
|
||||
(check-equal? (length in-vr) 5))
|
||||
|
||||
;Trace compilation tests
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event 0 0 'create 0 #f 0))
|
||||
(indexed-fevent 1 (future-event 0 1 'start-work 1 #f #f))
|
||||
(indexed-fevent 2 (future-event 0 1 'end-work 2 #f #f))
|
||||
(indexed-fevent 3 (future-event 0 0 'complete 3 #f #f)))]
|
||||
[organized (organize-output future-log)])
|
||||
(check-equal? (vector-length organized) 2)
|
||||
(let ([proc0log (vector-ref organized 0)]
|
||||
[proc1log (vector-ref organized 1)])
|
||||
(check-equal? (vector-length proc0log) 2)
|
||||
(check-equal? (vector-length proc1log) 2)))
|
||||
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event #f 0 'create 0 #f 0))
|
||||
(indexed-fevent 1 (future-event 0 1 'start-work 1 #f #f))
|
||||
(indexed-fevent 2 (future-event 0 1 'end-work 2 #f #f))
|
||||
(indexed-fevent 3 (future-event 0 0 'complete 3 #f #f)))]
|
||||
[trace (build-trace future-log)]
|
||||
[evts (trace-all-events trace)])
|
||||
(check-equal? (length evts) 4)
|
||||
(check-equal? (length (filter (λ (e) (event-next-future-event e)) evts)) 2)
|
||||
(check-equal? (length (filter (λ (e) (event-next-targ-future-event e)) evts)) 1)
|
||||
(check-equal? (length (filter (λ (e) (event-prev-targ-future-event e)) evts)) 1))
|
||||
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event 0 0 'create 0 #f 0))
|
||||
(indexed-fevent 1 (future-event 1 0 'create 1 #f 1))
|
||||
(indexed-fevent 2 (future-event 0 1 'start-work 2 #f #f))
|
||||
(indexed-fevent 3 (future-event 1 2 'start-work 2 #f #f))
|
||||
(indexed-fevent 4 (future-event 0 1 'end-work 4 #f #f))
|
||||
(indexed-fevent 5 (future-event 0 0 'complete 5 #f #f))
|
||||
(indexed-fevent 6 (future-event 1 2 'end-work 5 #f #f))
|
||||
(indexed-fevent 7 (future-event 1 0 'complete 7 #f #f)))]
|
||||
[organized (organize-output future-log)])
|
||||
(check-equal? (vector-length organized) 3)
|
||||
(let ([proc0log (vector-ref organized 0)]
|
||||
[proc1log (vector-ref organized 1)]
|
||||
[proc2log (vector-ref organized 2)])
|
||||
(check-equal? (vector-length proc0log) 4)
|
||||
(check-equal? (vector-length proc1log) 2)
|
||||
(check-equal? (vector-length proc2log) 2)
|
||||
(for ([msg (in-vector (vector-map indexed-fevent-fevent proc0log))])
|
||||
(check-equal? (future-event-process-id msg) 0))
|
||||
(for ([msg (in-vector (vector-map indexed-fevent-fevent proc1log))])
|
||||
(check-equal? (future-event-process-id msg) 1))
|
||||
(for ([msg (in-vector (vector-map indexed-fevent-fevent proc2log))])
|
||||
(check-equal? (future-event-process-id msg) 2))))
|
||||
|
||||
;Drawing calculation tests
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event #f 0 'create 0 #f 0))
|
||||
(indexed-fevent 1 (future-event 0 1 'start-work 1 #f #f))
|
||||
(indexed-fevent 2 (future-event 0 1 'end-work 2 #f #f))
|
||||
(indexed-fevent 3 (future-event 0 0 'complete 3 #f #f)))]
|
||||
[trace (build-trace future-log)])
|
||||
(let-values ([(finfo segments) (calc-segments trace)])
|
||||
(check-equal? (length segments) 4)
|
||||
(check-equal? (length (filter (λ (s) (segment-next-future-seg s)) segments)) 2)
|
||||
(check-equal? (length (filter (λ (s) (segment-next-targ-future-seg s)) segments)) 1)
|
||||
(check-equal? (length (filter (λ (s) (segment-prev-targ-future-seg s)) segments)) 1)))
|
||||
|
||||
;Future=42
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event #f 0 'create 0.05 #f 42))
|
||||
(indexed-fevent 1 (future-event 42 1 'start-work 0.07 #f #f))
|
||||
(indexed-fevent 2 (future-event 42 1 'end-work 0.3 #f #f))
|
||||
(indexed-fevent 3 (future-event 42 0 'complete 1.2 #f #f)))]
|
||||
[tr (build-trace future-log)])
|
||||
(define-values (finfo segs) (calc-segments tr))
|
||||
(define ticks (frame-info-timeline-ticks finfo))
|
||||
(check-equal? (length ticks) 11))
|
||||
|
||||
(define (check-seg-layout tr segs ticks)
|
||||
(define (do-seg-check seg tick op adjective)
|
||||
(define evt (segment-event seg))
|
||||
(check-true (op (segment-x seg) (timeline-tick-x tick))
|
||||
(format "Event at time ~a [x:~a] (~a) should be ~a tick at time ~a [x:~a]"
|
||||
(relative-time tr (event-start-time evt))
|
||||
(segment-x seg)
|
||||
(event-type evt)
|
||||
adjective
|
||||
(exact->inexact (timeline-tick-rel-time tick))
|
||||
(timeline-tick-x tick))))
|
||||
(for ([seg (in-list segs)])
|
||||
(let* ([evt (segment-event seg)]
|
||||
[evt-rel-time (relative-time tr (event-start-time evt))])
|
||||
(for ([tick (in-list ticks)])
|
||||
(let ([ttime (timeline-tick-rel-time tick)])
|
||||
(cond
|
||||
[(< evt-rel-time ttime)
|
||||
(do-seg-check seg tick <= "before")]
|
||||
[(= evt-rel-time ttime)
|
||||
(do-seg-check seg tick = "equal to")]
|
||||
[(> evt-rel-time ttime)
|
||||
(do-seg-check seg tick >= "after")]))))))
|
||||
|
||||
;Test layout for 'bad' mandelbrot trace
|
||||
(let-values ([(tr finfo segs ticks) (compile-trace-data BAD-TRACE-1)])
|
||||
(check-seg-layout tr segs ticks))
|
||||
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event #f 0 'create 0.05 #f 42))
|
||||
(indexed-fevent 1 (future-event 42 1 'start-work 0.09 #f #f))
|
||||
(indexed-fevent 2 (future-event 42 1 'suspend 1.1 #f #f))
|
||||
(indexed-fevent 3 (future-event 42 1 'resume 1.101 #f #f))
|
||||
(indexed-fevent 4 (future-event 42 1 'suspend 1.102 #f #f))
|
||||
(indexed-fevent 5 (future-event 42 1 'resume 1.103 #f #f))
|
||||
(indexed-fevent 6 (future-event 42 1 'start-work 1.104 #f #f))
|
||||
(indexed-fevent 7 (future-event 42 1 'complete 1.41 #f #f))
|
||||
(indexed-fevent 8 (future-event 42 1 'end-work 1.42 #f #f))
|
||||
(indexed-fevent 9 (future-event 42 0 'result 1.43 #f #f)))]
|
||||
[tr (build-trace future-log)])
|
||||
(define-values (finfo segs) (calc-segments tr))
|
||||
(define ticks (frame-info-timeline-ticks finfo))
|
||||
(check-seg-layout tr segs ticks))
|
||||
|
||||
(let* ([future-log (list (indexed-fevent 0 (future-event #f 0 'create 0 #f 0))
|
||||
(indexed-fevent 1 (future-event 0 1 'start-work 1 #f #f))
|
||||
(indexed-fevent 2 (future-event 0 1 'end-work 2 #f #f))
|
||||
(indexed-fevent 3 (future-event 0 0 'complete 3 #f #f)))]
|
||||
[trace (build-trace future-log)])
|
||||
(check-equal? (trace-start-time trace) 0)
|
||||
(check-equal? (trace-end-time trace) 3)
|
||||
(check-equal? (length (trace-proc-timelines trace)) 2)
|
||||
(check-equal? (trace-real-time trace) 3)
|
||||
(check-equal? (trace-num-futures trace) 1)
|
||||
(check-equal? (trace-num-blocks trace) 0)
|
||||
(check-equal? (trace-num-syncs trace) 0)
|
||||
(let ([proc0tl (list-ref (trace-proc-timelines trace) 0)]
|
||||
[proc1tl (list-ref (trace-proc-timelines trace) 1)])
|
||||
(check-equal? (process-timeline-start-time proc0tl) 0)
|
||||
(check-equal? (process-timeline-end-time proc0tl) 3)
|
||||
(check-equal? (process-timeline-start-time proc1tl) 1)
|
||||
(check-equal? (process-timeline-end-time proc1tl) 2)
|
||||
(let ([proc0segs (process-timeline-events proc0tl)]
|
||||
[proc1segs (process-timeline-events proc1tl)])
|
||||
(check-equal? (length proc0segs) 2)
|
||||
(check-equal? (length proc1segs) 2)
|
||||
(check-equal? (event-timeline-position (list-ref proc0segs 0)) 'start)
|
||||
(check-equal? (event-timeline-position (list-ref proc0segs 1)) 'end))))
|
||||
|
||||
;Viewable region tests
|
||||
(define (make-seg-at x y w h)
|
||||
(segment #f x y w h #f #f #f #f #f #f #f #f))
|
||||
|
||||
;;make-segs-with-times : (listof (or float (float . float))) -> (listof segment)
|
||||
(define (make-segs-with-times . times)
|
||||
(for/list ([t (in-list times)] [i (in-naturals)])
|
||||
(if (pair? t)
|
||||
(make-seg-with-time i (car t) #:end-time (cdr t))
|
||||
(make-seg-with-time i t))))
|
||||
|
||||
;;make-seg-with-time : fixnum float [float] -> segment
|
||||
(define (make-seg-with-time index real-start-time #:end-time [real-end-time real-start-time])
|
||||
(segment (event index
|
||||
real-start-time
|
||||
real-end-time
|
||||
0 0 0 0 0 0 0 0 0 0 0 0 0 #f)
|
||||
0 0 0 0 #f #f #f #f #f #f #f #f))
|
||||
|
||||
|
||||
(let ([vregion (viewable-region 20 30 100 100)]
|
||||
[seg1 (make-seg-at 0 5 10 10)]
|
||||
[seg2 (make-seg-at 20 30 5 5)]
|
||||
[seg3 (make-seg-at 150 35 5 5)])
|
||||
(check-false ((seg-in-vregion vregion) seg1))
|
||||
(check-true ((seg-in-vregion vregion) seg2))
|
||||
(check-false ((seg-in-vregion vregion) seg3)))
|
||||
|
||||
;segs-equal-or-later
|
||||
(let ([segs (make-segs-with-times 0.1
|
||||
0.3
|
||||
1.2
|
||||
(cons 1.4 1.9)
|
||||
2.4
|
||||
2.8
|
||||
3.1)])
|
||||
(check-equal? (length (segs-equal-or-later 0.1 segs)) 7)
|
||||
(check-equal? (length (segs-equal-or-later 0.3 segs)) 6)
|
||||
(check-equal? (length (segs-equal-or-later 1.2 segs)) 5)
|
||||
(check-equal? (length (segs-equal-or-later 1.4 segs)) 4)
|
||||
(check-equal? (length (segs-equal-or-later 2.4 segs)) 3)
|
||||
(check-equal? (length (segs-equal-or-later 2.8 segs)) 2)
|
||||
(check-equal? (length (segs-equal-or-later 3.1 segs)) 1)
|
||||
(check-equal? (length (segs-equal-or-later 4.0 segs)) 0))
|
||||
|
||||
;Tick drawing
|
||||
(let ([l (list (indexed-fevent 0 (future-event #f 0 'create 10.0 #f 0))
|
||||
(indexed-fevent 1 (future-event 0 0 'start-work 11.0 #f #f))
|
||||
(indexed-fevent 2 (future-event 0 0 'end-work 20.0 #f #f)))])
|
||||
(define-values (tr finfo segs ticks) (compile-trace-data l))
|
||||
;Check that number of ticks stays constant whatever the time->pixel modifier
|
||||
(check-equal? (length ticks) 100)
|
||||
(check-equal? (length (calc-ticks segs 700 tr)) 100)
|
||||
(for ([i (in-range 0.1 20)])
|
||||
(check-equal? (length (calc-ticks segs i tr))
|
||||
100
|
||||
(format "Wrong number of ticks for time->pix mod ~a\n" i)))
|
||||
(check-seg-layout tr segs ticks))
|
||||
|
||||
(let ([l (list (indexed-fevent 0 '#s(future-event #f 0 create 1334778395768.733 #f 3))
|
||||
(indexed-fevent 1 '#s(future-event 3 2 start-work 1334778395768.771 #f #f))
|
||||
(indexed-fevent 2 '#s(future-event 3 2 complete 1334778395864.648 #f #f))
|
||||
(indexed-fevent 3 '#s(future-event 3 2 end-work 1334778395864.652 #f #f)))])
|
||||
(define-values (tr finfo segs ticks) (compile-trace-data l))
|
||||
(define last-evt (indexed-fevent-fevent (list-ref l 3)))
|
||||
(define first-evt (indexed-fevent-fevent (list-ref l 0)))
|
||||
(define total-time (- (future-event-time last-evt) (future-event-time first-evt)))
|
||||
(check-equal? (length ticks) (inexact->exact (floor (* 10 total-time)))))
|
||||
|
||||
(define mand-first
|
||||
(list (indexed-fevent 0 '#s(future-event #f 0 create 1334779294212.415 #f 1))
|
||||
(indexed-fevent 1 '#s(future-event 1 1 start-work 1334779294212.495 #f #f))
|
||||
(indexed-fevent 2 '#s(future-event 1 1 sync 1334779294212.501 #f #f))
|
||||
(indexed-fevent 3 (future-event 1 0 'sync 1334779294221.128 'allocate_memory #f))
|
||||
(indexed-fevent 4 '#s(future-event 1 0 result 1334779294221.138 #f #f))
|
||||
(indexed-fevent 5 '#s(future-event 1 1 result 1334779294221.15 #f #f))))
|
||||
(let-values ([(tr finfo segs ticks) (compile-trace-data mand-first)])
|
||||
(check-seg-layout tr segs ticks))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
98
qsort2.rkt
Normal file
98
qsort2.rkt
Normal file
|
@ -0,0 +1,98 @@
|
|||
#lang racket/base
|
||||
|
||||
(require racket/future
|
||||
racket/future/visualizer
|
||||
rackunit
|
||||
racket/fixnum
|
||||
racket/unsafe/ops
|
||||
(only-in racket/list empty?)
|
||||
racket/vector
|
||||
rackunit)
|
||||
|
||||
(define (qsort v)
|
||||
(define length (vector-length v))
|
||||
(qsort-h v 0 (sub1 length)))
|
||||
|
||||
(define (qsort-h v l r)
|
||||
(when (unsafe-fx> (unsafe-fx- r l) 0)
|
||||
(define m (partition v l r))
|
||||
(qsort-h v l (unsafe-fx- m 1))
|
||||
(qsort-h v (unsafe-fx+ 1 m) r)))
|
||||
|
||||
(define (swap! v i j)
|
||||
(define temp (unsafe-vector-ref v j))
|
||||
(unsafe-vector-set! v j (unsafe-vector-ref v i))
|
||||
(unsafe-vector-set! v i temp))
|
||||
|
||||
(define (partition v l r)
|
||||
(swap! v l r)
|
||||
(define p (unsafe-vector-ref v r))
|
||||
;moving this inline eliminates jit copilations
|
||||
;but slows things down and makes things less parallel (factor of ~1.5)
|
||||
(let recur
|
||||
([i l]
|
||||
[p-i l])
|
||||
(cond
|
||||
[(unsafe-fx= i r)
|
||||
(swap! v r p-i)
|
||||
p-i]
|
||||
[(unsafe-fx< (unsafe-vector-ref v i) p)
|
||||
(swap! v i p-i)
|
||||
(recur (unsafe-fx+ 1 i) (unsafe-fx+ p-i 1))]
|
||||
[else
|
||||
(recur (unsafe-fx+ 1 i) p-i)])))
|
||||
|
||||
;(define (partition v l r)
|
||||
; (let recur ([i (fx- l 1)]
|
||||
; [j l])
|
||||
; (cond
|
||||
; [(fx= j (fx- r 1))
|
||||
; (swap! v (fx+ i 1) r)
|
||||
; (fx+ i 1)]
|
||||
; [(fx< (unsafe-vector-ref v j) (unsafe-vector-ref v r))
|
||||
; (swap! v (fx+ i 1) j)
|
||||
; (recur (fx+ i 1) (fx+ j 1))]
|
||||
; [else
|
||||
; (recur i (fx+ j 1))])))
|
||||
|
||||
(define (qsort-f v)
|
||||
(define length (vector-length v))
|
||||
(qsort-f-h v 0 (sub1 length) 6))
|
||||
|
||||
(define (qsort-f-h v l r d)
|
||||
(when (unsafe-fx> (unsafe-fx- r l) 0)
|
||||
;(define m (partition v l r))
|
||||
(if (unsafe-fx= d 0)
|
||||
(let ([m (partition v l r)])
|
||||
(qsort-h v l (unsafe-fx- m 1))
|
||||
(qsort-h v (unsafe-fx+ 1 m) r))
|
||||
(let* ([m (partition v l r)]
|
||||
[f1 (future (λ () (qsort-f-h v l (unsafe-fx- m 1) (unsafe-fx- d 1))))]
|
||||
[f2 (future (λ () (qsort-f-h v (unsafe-fx+ 1 m) r (unsafe-fx- d 1))))])
|
||||
(touch f1)
|
||||
(touch f2)))))
|
||||
|
||||
(define (check-sorting v)
|
||||
(define l (vector->list v))
|
||||
(define v1 (list->vector (sort l <)))
|
||||
(qsort-f v)
|
||||
(check-equal? v1 v))
|
||||
|
||||
(define (rand-v size) (build-vector size (λ (_) (random 30))))
|
||||
|
||||
;(check-sorting (rand-v 100))
|
||||
|
||||
(random-seed 2)
|
||||
|
||||
(define v1 (rand-v 50000))
|
||||
(define v2 (vector-copy v1))
|
||||
|
||||
#;(time (begin (sort (vector->list v1) <)
|
||||
'a))
|
||||
#;(time (begin (qsort v1)
|
||||
'b))
|
||||
(let ()
|
||||
(start-performance-tracking!)
|
||||
(time (qsort-f v2))
|
||||
(show-visualizer)
|
||||
'c)
|
|
@ -39,7 +39,7 @@ Scheme_Object *scheme_fsemaphore_p(int argc, Scheme_Object *argv[])
|
|||
|
||||
static Scheme_Object *futures_enabled(int argc, Scheme_Object *argv[])
|
||||
{
|
||||
#ifdef MZ_USE_FUTURES
|
||||
#ifdef MZ_USE_FUTURESRACKET
|
||||
return scheme_true;
|
||||
#else
|
||||
return scheme_false;
|
||||
|
@ -312,6 +312,7 @@ static int capture_future_continuation(struct Scheme_Future_State *fs, future_t
|
|||
#define FUTURE_RUNSTACK_SIZE 2000
|
||||
|
||||
#define FEVENT_BUFFER_SIZE 512
|
||||
#define NO_FUTURE_ID -1
|
||||
|
||||
enum {
|
||||
FEVENT_CREATE,
|
||||
|
@ -589,6 +590,7 @@ void futures_init(void)
|
|||
rt_fts->is_runtime_thread = 1;
|
||||
rt_fts->gen0_size = 1;
|
||||
scheme_future_thread_state = rt_fts;
|
||||
rt_fts->thread = scheme_current_thread;
|
||||
|
||||
REGISTER_SO(fs->future_queue);
|
||||
REGISTER_SO(fs->future_queue_end);
|
||||
|
@ -617,7 +619,7 @@ void futures_init(void)
|
|||
syms[FEVENT_HANDLE_RTCALL] = sym;
|
||||
|
||||
sym = scheme_intern_symbol("future-event");
|
||||
stype = scheme_lookup_prefab_type(sym, 5);
|
||||
stype = scheme_lookup_prefab_type(sym, 6);
|
||||
fs->fevent_prefab = stype;
|
||||
|
||||
init_fevent(&fs->runtime_fevents);
|
||||
|
@ -968,8 +970,8 @@ static void free_fevent(Fevent_Buffer *b)
|
|||
}
|
||||
}
|
||||
|
||||
static void record_fevent(int what, int fid) XFORM_SKIP_PROC
|
||||
/* call with the lock or in the runtime thread */
|
||||
static void record_fevent_with_data(int what, int fid, int data)
|
||||
XFORM_SKIP_PROC
|
||||
{
|
||||
Scheme_Future_Thread_State *fts = scheme_future_thread_state;
|
||||
Fevent_Buffer *b;
|
||||
|
@ -985,6 +987,7 @@ static void record_fevent(int what, int fid) XFORM_SKIP_PROC
|
|||
b->a[b->pos].timestamp = get_future_timestamp();
|
||||
b->a[b->pos].what = what;
|
||||
b->a[b->pos].fid = fid;
|
||||
b->a[b->pos].data = data;
|
||||
|
||||
b->pos++;
|
||||
if (b->pos == FEVENT_BUFFER_SIZE) {
|
||||
|
@ -993,6 +996,12 @@ static void record_fevent(int what, int fid) XFORM_SKIP_PROC
|
|||
}
|
||||
}
|
||||
|
||||
static void record_fevent(int what, int fid) XFORM_SKIP_PROC
|
||||
/* call with the lock or in the runtime thread */
|
||||
{
|
||||
record_fevent_with_data(what, fid, 0);
|
||||
}
|
||||
|
||||
static void init_traversal(Fevent_Buffer *b)
|
||||
{
|
||||
if (b->overflow) {
|
||||
|
@ -1010,18 +1019,19 @@ static void end_traversal(Fevent_Buffer *b)
|
|||
b->pos = 0;
|
||||
}
|
||||
|
||||
static void log_future_event(Scheme_Future_State *fs,
|
||||
const char *msg_str,
|
||||
const char *extra_str,
|
||||
int which,
|
||||
int what,
|
||||
double timestamp,
|
||||
int fid)
|
||||
static void log_future_event_with_data(Scheme_Future_State *fs,
|
||||
const char *msg_str,
|
||||
const char *extra_str,
|
||||
int which,
|
||||
int what,
|
||||
double timestamp,
|
||||
int fid,
|
||||
int user_data)
|
||||
{
|
||||
Scheme_Object *data, *v;
|
||||
|
||||
data = scheme_make_blank_prefab_struct_instance(fs->fevent_prefab);
|
||||
if (what == FEVENT_MISSING)
|
||||
if (what == FEVENT_MISSING || fid == NO_FUTURE_ID)
|
||||
((Scheme_Structure *)data)->slots[0] = scheme_false;
|
||||
else
|
||||
((Scheme_Structure *)data)->slots[0] = scheme_make_integer(fid);
|
||||
|
@ -1034,11 +1044,14 @@ static void log_future_event(Scheme_Future_State *fs,
|
|||
((Scheme_Structure *)data)->slots[2] = v;
|
||||
v = scheme_make_double(timestamp);
|
||||
((Scheme_Structure *)data)->slots[3] = v;
|
||||
if (what == FEVENT_HANDLE_RTCALL) {
|
||||
if (what == FEVENT_HANDLE_RTCALL || what == FEVENT_HANDLE_RTCALL_ATOMIC) {
|
||||
v = scheme_intern_symbol(extra_str);
|
||||
((Scheme_Structure *)data)->slots[4] = v;
|
||||
} else
|
||||
((Scheme_Structure *)data)->slots[4] = scheme_false;
|
||||
|
||||
/* User data (target fid for creates, alloc amount for allocation */
|
||||
((Scheme_Structure *)data)->slots[5] = scheme_make_integer(user_data);
|
||||
|
||||
scheme_log_w_data(scheme_main_logger, SCHEME_LOG_DEBUG, 0,
|
||||
data,
|
||||
|
@ -1048,6 +1061,25 @@ static void log_future_event(Scheme_Future_State *fs,
|
|||
fevent_long_strs[what],
|
||||
extra_str,
|
||||
timestamp);
|
||||
|
||||
}
|
||||
|
||||
static void log_future_event(Scheme_Future_State *fs,
|
||||
const char *msg_str,
|
||||
const char *extra_str,
|
||||
int which,
|
||||
int what,
|
||||
double timestamp,
|
||||
int fid)
|
||||
{
|
||||
log_future_event_with_data(fs,
|
||||
msg_str,
|
||||
extra_str,
|
||||
which,
|
||||
what,
|
||||
timestamp,
|
||||
fid,
|
||||
0);
|
||||
}
|
||||
|
||||
static void log_overflow_event(Scheme_Future_State *fs, int which, double timestamp)
|
||||
|
@ -1135,13 +1167,14 @@ static void flush_future_logs(Scheme_Future_State *fs)
|
|||
if (!min_b)
|
||||
break;
|
||||
|
||||
log_future_event(fs,
|
||||
log_future_event_with_data(fs,
|
||||
"future %d, process %d: %s%s; time: %f",
|
||||
"",
|
||||
min_which,
|
||||
min_b->a[min_b->i].what,
|
||||
min_b->a[min_b->i].timestamp,
|
||||
min_b->a[min_b->i].fid);
|
||||
min_b->a[min_b->i].fid,
|
||||
min_b->a[min_b->i].data);
|
||||
|
||||
--min_b->count;
|
||||
min_b->i++;
|
||||
|
@ -1176,7 +1209,7 @@ void scheme_wrong_contract_from_ft(const char *who, const char *expected_type, i
|
|||
scheme_wrong_contract(who, expected_type, what, argc, argv);
|
||||
|
||||
|
||||
static Scheme_Object *make_future(Scheme_Object *lambda, int enqueue)
|
||||
static Scheme_Object *make_future(Scheme_Object *lambda, int enqueue, future_t *cur_ft)
|
||||
/* Called in runtime thread --- as atomic on behalf of a future thread
|
||||
if `lambda' is known to be a thunk */
|
||||
{
|
||||
|
@ -1225,7 +1258,7 @@ static Scheme_Object *make_future(Scheme_Object *lambda, int enqueue)
|
|||
mzrt_mutex_lock(fs->future_mutex);
|
||||
futureid = ++fs->next_futureid;
|
||||
ft->id = futureid;
|
||||
record_fevent(FEVENT_CREATE, futureid);
|
||||
record_fevent_with_data(FEVENT_CREATE, (cur_ft ? cur_ft->id : NO_FUTURE_ID), futureid);
|
||||
if (enqueue) {
|
||||
if (ft->status != PENDING_OVERSIZE)
|
||||
enqueue_future(fs, ft);
|
||||
|
@ -1244,18 +1277,24 @@ int scheme_can_apply_native_in_future(Scheme_Object *proc)
|
|||
return (((Scheme_Native_Closure *)proc)->code->max_let_depth < FUTURE_RUNSTACK_SIZE * sizeof(void*));
|
||||
}
|
||||
|
||||
static Scheme_Object *do_make_future(int argc, Scheme_Object *argv[])
|
||||
static Scheme_Object *do_make_future(int argc, Scheme_Object *argv[], future_t *cur_ft)
|
||||
{
|
||||
Scheme_Future_State *fs;
|
||||
scheme_check_proc_arity("future", 0, 0, argc, argv);
|
||||
return make_future(argv[0], 1);
|
||||
|
||||
fs = scheme_future_state;
|
||||
flush_future_logs(fs);
|
||||
|
||||
return make_future(argv[0], 1, cur_ft);
|
||||
}
|
||||
|
||||
Scheme_Object *scheme_future(int argc, Scheme_Object *argv[])
|
||||
XFORM_SKIP_PROC /* can be called from future thread */
|
||||
{
|
||||
Scheme_Future_Thread_State *fts = scheme_future_thread_state;
|
||||
if (fts->is_runtime_thread)
|
||||
return do_make_future(argc, argv);
|
||||
if (fts->is_runtime_thread) {
|
||||
return do_make_future(argc, argv, (scheme_current_thread ? scheme_current_thread->current_ft : NULL));
|
||||
}
|
||||
else {
|
||||
Scheme_Object *proc = argv[0];
|
||||
#ifdef MZ_PRECISE_GC
|
||||
|
@ -1267,6 +1306,7 @@ Scheme_Object *scheme_future(int argc, Scheme_Object *argv[])
|
|||
future_t *ft;
|
||||
ft = MALLOC_ONE_TAGGED(future_t);
|
||||
if (ft) {
|
||||
future_t *cur_ft = scheme_current_thread->current_ft;
|
||||
Scheme_Future_State *fs = scheme_future_state;
|
||||
|
||||
ft->so.type = scheme_future_type;
|
||||
|
@ -1276,7 +1316,7 @@ Scheme_Object *scheme_future(int argc, Scheme_Object *argv[])
|
|||
|
||||
mzrt_mutex_lock(fs->future_mutex);
|
||||
ft->id = ++fs->next_futureid;
|
||||
record_fevent(FEVENT_CREATE, ft->id);
|
||||
record_fevent_with_data(FEVENT_CREATE, (cur_ft ? cur_ft->id : NO_FUTURE_ID), ft->id);
|
||||
enqueue_future(fs, ft);
|
||||
mzrt_mutex_unlock(fs->future_mutex);
|
||||
|
||||
|
@ -1301,9 +1341,11 @@ static Scheme_Object *would_be_future(int argc, Scheme_Object *argv[])
|
|||
/* Called in runtime thread */
|
||||
{
|
||||
future_t *ft;
|
||||
Scheme_Future_Thread_State *fts;
|
||||
scheme_check_proc_arity("would-be-future", 0, 0, argc, argv);
|
||||
|
||||
ft = (future_t*)make_future(argv[0], 0);
|
||||
fts = scheme_future_thread_state;
|
||||
|
||||
ft = (future_t*)make_future(argv[0], 0, (fts->thread ? fts->thread->current_ft : NULL));
|
||||
ft->in_tracing_mode = 1;
|
||||
ft->fts = scheme_future_thread_state;
|
||||
|
||||
|
@ -1779,11 +1821,15 @@ static Scheme_Object *shallower_apply_future_lw_k(void)
|
|||
static int future_in_runtime(Scheme_Future_State *fs, future_t * volatile ft, int what)
|
||||
{
|
||||
mz_jmp_buf newbuf, * volatile savebuf;
|
||||
Scheme_Thread *p = scheme_current_thread;
|
||||
Scheme_Thread *p = scheme_current_thread;
|
||||
Scheme_Object * volatile retval;
|
||||
future_t * volatile old_ft;
|
||||
int done;
|
||||
|
||||
//FUTURE_ASSERT((!scheme_future_thread_state && !p->current_ft) || scheme_future_thread_state);
|
||||
//FUTURE_ASSERT(scheme_future_thread_state->thread == p);
|
||||
//FUTURE_ASSERT(scheme_future_thread_state->thread->current_ft == p->current_ft);
|
||||
|
||||
old_ft = p->current_ft;
|
||||
p->current_ft = ft;
|
||||
|
||||
|
@ -1990,17 +2036,20 @@ Scheme_Object *touch(int argc, Scheme_Object *argv[])
|
|||
Scheme_Future_Thread_State *fts = scheme_future_thread_state;
|
||||
if (fts->is_runtime_thread) {
|
||||
future_t *ft;
|
||||
future_t *targ_ft;
|
||||
if (fts->thread
|
||||
&& (ft = fts->thread->current_ft)
|
||||
&& ft->in_tracing_mode) {
|
||||
targ_ft = (future_t*)argv[0];
|
||||
Scheme_Future_State *fs = scheme_future_state;
|
||||
log_future_event( fs,
|
||||
"future %d, process %d: %s: %s; time: %f",
|
||||
"touch",
|
||||
-1,
|
||||
FEVENT_RTCALL_TOUCH,
|
||||
get_future_timestamp(),
|
||||
ft->id);
|
||||
log_future_event_with_data( fs,
|
||||
"future %d, process %d: %s: %s; time: %f",
|
||||
"touch",
|
||||
-1,
|
||||
FEVENT_RTCALL_TOUCH,
|
||||
get_future_timestamp(),
|
||||
ft->id,
|
||||
targ_ft->id);
|
||||
}
|
||||
|
||||
return general_touch(argc, argv);
|
||||
|
@ -2658,6 +2707,8 @@ static void future_do_runtimecall(Scheme_Future_Thread_State *fts,
|
|||
runtime thread so we can log all of its primitive applications). */
|
||||
{
|
||||
future_t *future;
|
||||
future_t *targ_future;
|
||||
Scheme_Object **prim_argv;
|
||||
Scheme_Future_State *fs = scheme_future_state;
|
||||
void *storage[4];
|
||||
int insist_to_suspend, prefer_to_suspend, fid;
|
||||
|
@ -2836,6 +2887,7 @@ static void future_do_runtimecall(Scheme_Future_Thread_State *fts,
|
|||
scheme_future_longjmp(*scheme_current_thread->error_buf, 1);
|
||||
} else {
|
||||
FUTURE_ASSERT(future->status == RUNNING);
|
||||
record_fevent(FEVENT_START_WORK, fid);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3270,14 +3322,25 @@ static void do_invoke_rtcall(Scheme_Future_State *fs, future_t *future)
|
|||
|
||||
flush_future_logs(fs);
|
||||
|
||||
/* use log_future_event so we can include `str' in the message: */
|
||||
log_future_event(fs,
|
||||
"future %d, process %d: %s: %s; time: %f",
|
||||
src,
|
||||
-1,
|
||||
(future->rt_prim_is_atomic ? FEVENT_HANDLE_RTCALL_ATOMIC : FEVENT_HANDLE_RTCALL),
|
||||
get_future_timestamp(),
|
||||
future->id);
|
||||
/* use lg_future_event so we can include `str' in the message: */
|
||||
if (future->prim_protocol == SIG_ALLOC) {
|
||||
log_future_event_with_data(fs,
|
||||
"future %d, process %d: %s: %s; time: %f",
|
||||
src,
|
||||
-1,
|
||||
(future->rt_prim_is_atomic ? FEVENT_HANDLE_RTCALL_ATOMIC : FEVENT_HANDLE_RTCALL),
|
||||
get_future_timestamp(),
|
||||
future->id,
|
||||
future->arg_i0);
|
||||
} else {
|
||||
log_future_event(fs,
|
||||
"future %d, process %d: %s: %s; time: %f",
|
||||
src,
|
||||
-1,
|
||||
(future->rt_prim_is_atomic ? FEVENT_HANDLE_RTCALL_ATOMIC : FEVENT_HANDLE_RTCALL),
|
||||
get_future_timestamp(),
|
||||
future->id);
|
||||
}
|
||||
}
|
||||
|
||||
if (((future->source_type == FSRC_RATOR)
|
||||
|
@ -3336,7 +3399,7 @@ static void do_invoke_rtcall(Scheme_Future_State *fs, future_t *future)
|
|||
{
|
||||
Scheme_Object *s = future->arg_s1;
|
||||
future->arg_s1 = NULL;
|
||||
s = make_future(s, 1);
|
||||
s = make_future(s, 1, future);
|
||||
future->retval_s = s;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ typedef void* (*prim_pvoid_pvoid_pvoid_t)(void*, void*);
|
|||
|
||||
typedef struct Fevent {
|
||||
double timestamp;
|
||||
int what, fid;
|
||||
int what, fid, data;
|
||||
} Fevent;
|
||||
|
||||
typedef struct Fevent_Buffer {
|
||||
|
|
Loading…
Reference in New Issue
Block a user