1
+ <!doctype html>
2
+ < html >
3
+ < head >
4
+ < link rel ="stylesheet " href ="uPlot.min.css ">
5
+ < style >
6
+ .uplot {
7
+ display : inline-block;
8
+ vertical-align : top;
9
+ width : min-content;
10
+ }
11
+
12
+ .u-over {
13
+ box-shadow : 0px 0px 0px 0.5px # ccc ;
14
+ }
15
+
16
+ .u-legend {
17
+ text-align : left;
18
+ padding-left : 50px ;
19
+ }
20
+
21
+ .u-inline tr {
22
+ margin-right : 8px ;
23
+ }
24
+
25
+ .u-label {
26
+ font-size : 12px ;
27
+ }
28
+
29
+ .u-tooltip {
30
+ font-size : 10pt ;
31
+ position : absolute;
32
+ background : # fff ;
33
+ display : none;
34
+ border : 2px solid black;
35
+ padding : 4px ;
36
+ pointer-events : none;
37
+ z-index : 100 ;
38
+ white-space : pre;
39
+ font-family : monospace;
40
+ }
41
+ </ style >
42
+ < script src ="uPlot.iife.min.js "> </ script >
43
+ </ head >
44
+ < body >
45
+ < script >
46
+ const seriesColors = [ "#7cb5ec" , "#434348" , "#90ed7d" , "#f7a35c" , "#8085e9" , "#f15c80" , "#e4d354" , "#2b908f" , "#f45b5b" , "#91e8e1" ] ;
47
+ const interpolatedColor = "#fcb0f1" ;
48
+
49
+ function tooltipPlugin ( { onclick, commits, isInterpolated, shiftX = 10 , shiftY = 10 } ) {
50
+ let tooltipLeftOffset = 0 ;
51
+ let tooltipTopOffset = 0 ;
52
+
53
+ const tooltip = document . createElement ( "div" ) ;
54
+ tooltip . className = "u-tooltip" ;
55
+
56
+ let seriesIdx = null ;
57
+ let dataIdx = null ;
58
+
59
+ const fmtDate = uPlot . fmtDate ( "{M}/{D}/{YY} {h}:{mm}:{ss} {AA}" ) ;
60
+
61
+ function setTooltip ( u ) {
62
+ let top = u . valToPos ( u . data [ seriesIdx ] [ dataIdx ] , 'y' ) ;
63
+ let lft = u . valToPos ( u . data [ 0 ] [ dataIdx ] , 'x' ) ;
64
+
65
+ tooltip . style . top = ( tooltipTopOffset + top + shiftX ) + "px" ;
66
+ tooltip . style . left = ( tooltipLeftOffset + lft + shiftY ) + "px" ;
67
+
68
+ tooltip . style . borderColor = isInterpolated ( dataIdx ) ? interpolatedColor : seriesColors [ seriesIdx - 1 ] ;
69
+ let pctSinceStart = ( ( ( u . data [ seriesIdx ] [ dataIdx ] - u . data [ seriesIdx ] [ 0 ] ) / u . data [ seriesIdx ] [ 0 ] ) * 100 ) . toFixed ( 2 ) ;
70
+ tooltip . textContent = (
71
+ fmtDate ( new Date ( u . data [ 0 ] [ dataIdx ] * 1e3 ) ) + " - " + commits [ dataIdx ] [ 1 ] . slice ( 0 , 10 ) + "\n" +
72
+ uPlot . fmtNum ( u . data [ seriesIdx ] [ dataIdx ] ) + "(" + pctSinceStart + "% since start)"
73
+ ) ;
74
+ }
75
+
76
+ return {
77
+ hooks : {
78
+ ready : [
79
+ u => {
80
+ let over = u . root . querySelector ( ".u-over" ) ;
81
+ tooltipLeftOffset = parseFloat ( over . style . left ) ;
82
+ tooltipTopOffset = parseFloat ( over . style . top ) ;
83
+ u . root . querySelector ( ".u-wrap" ) . appendChild ( tooltip ) ;
84
+
85
+ let clientX ;
86
+ let clientY ;
87
+
88
+ over . addEventListener ( "mousedown" , e => {
89
+ clientX = e . clientX ;
90
+ clientY = e . clientY ;
91
+ } ) ;
92
+
93
+ over . addEventListener ( "mouseup" , e => {
94
+ // clicked in-place
95
+ if ( e . clientX == clientX && e . clientY == clientY ) {
96
+ if ( seriesIdx != null && dataIdx != null ) {
97
+ onclick ( u , seriesIdx , dataIdx ) ;
98
+ }
99
+ }
100
+ } ) ;
101
+ }
102
+ ] ,
103
+ setCursor : [
104
+ u => {
105
+ let c = u . cursor ;
106
+
107
+ if ( c . idx != dataIdx ) {
108
+ dataIdx = c . idx ;
109
+
110
+ if ( seriesIdx != null )
111
+ setTooltip ( u , setTooltip ) ;
112
+ }
113
+ }
114
+ ] ,
115
+ setSeries : [
116
+ ( u , sidx ) => {
117
+ if ( seriesIdx != sidx ) {
118
+ seriesIdx = sidx ;
119
+
120
+ if ( sidx == null )
121
+ tooltip . style . display = "none" ;
122
+ else if ( dataIdx != null ) {
123
+ tooltip . style . display = "block" ;
124
+ setTooltip ( u ) ;
125
+ }
126
+ }
127
+ }
128
+ ] ,
129
+ }
130
+ } ;
131
+ }
132
+
133
+ function genPlotOpts ( { title, width, height, yAxisLabel, series, commits, stat, isInterpolated, alpha = 0.3 , prox = 5 } ) {
134
+ return {
135
+ title,
136
+ width,
137
+ height,
138
+ series,
139
+ legend : {
140
+ live : false ,
141
+ } ,
142
+ focus : {
143
+ alpha,
144
+ } ,
145
+ cursor : {
146
+ focus : {
147
+ prox,
148
+ } ,
149
+ drag : {
150
+ x : true ,
151
+ y : true ,
152
+ } ,
153
+ } ,
154
+ scales : {
155
+ y : {
156
+ range : ( self , dataMin , dataMax ) => uPlot . rangeNum ( 0 , dataMax , 0.2 , true )
157
+ }
158
+ } ,
159
+ axes : [
160
+ {
161
+ grid : {
162
+ show : false ,
163
+ }
164
+ } ,
165
+ {
166
+ label : yAxisLabel ,
167
+ space : 24 ,
168
+ values : ( self , splits ) => {
169
+ return splits . map ( v => {
170
+ return (
171
+ v >= 1e12 ? v / 1e12 + "T" :
172
+ v >= 1e9 ? v / 1e9 + "G" :
173
+ v >= 1e6 ? v / 1e6 + "M" :
174
+ v >= 1e3 ? v / 1e3 + "k" :
175
+ v
176
+ ) ;
177
+ } ) ;
178
+ } ,
179
+ } ,
180
+ ] ,
181
+ plugins : [
182
+ tooltipPlugin ( {
183
+ onclick ( u , seriesIdx , dataIdx ) {
184
+ let thisCommit = commits [ dataIdx ] [ 1 ] ;
185
+ let prevCommit = ( commits [ dataIdx - 1 ] || [ null , null ] ) [ 1 ] ;
186
+ window . open ( `https://perf.rust-lang.org/compare.html?start=${ prevCommit } &end=${ thisCommit } &stat=${ stat } ` ) ;
187
+ } ,
188
+ commits,
189
+ isInterpolated,
190
+ } ) ,
191
+ ] ,
192
+ } ;
193
+ }
194
+
195
+ function renderPlots ( data , state ) {
196
+ for ( let benchName in data . benchmarks ) {
197
+ let benchKinds = data . benchmarks [ benchName ] ;
198
+
199
+ let i = 0 ;
200
+
201
+ for ( let benchKind in benchKinds ) {
202
+ let cacheStates = benchKinds [ benchKind ] ;
203
+
204
+ let yAxisLabel = i == 0 ? "Value" : null ;
205
+
206
+ let seriesOpts = [ { } ] ;
207
+
208
+ let xVals = data . commits . map ( c => c [ 0 ] ) ;
209
+
210
+ let plotData = [ xVals ] ;
211
+
212
+ let j = 0 ;
213
+
214
+ for ( let cacheState in cacheStates ) {
215
+ let yVals = cacheStates [ cacheState ] . points ;
216
+
217
+ plotData . push ( yVals ) ;
218
+
219
+ seriesOpts . push ( {
220
+ label : cacheState ,
221
+ width : devicePixelRatio ,
222
+ stroke : seriesColors [ j ] ,
223
+ } ) ;
224
+
225
+ j ++ ;
226
+ }
227
+
228
+ let plotOpts = genPlotOpts ( {
229
+ title : benchName + "-" + benchKind ,
230
+ width : Math . floor ( window . innerWidth / 3 ) - 16 ,
231
+ height : 300 ,
232
+ yAxisLabel,
233
+ series : seriesOpts ,
234
+ commits : data . commits ,
235
+ stat : state . stat ,
236
+ isInterpolated ( dataIdx ) {
237
+ return cacheStates . full . is_interpolated . has ( dataIdx ) ;
238
+ } ,
239
+ } ) ;
240
+
241
+ let u = new uPlot ( plotOpts , plotData , document . body ) ;
242
+
243
+ i ++ ;
244
+ }
245
+ }
246
+ }
247
+
248
+ const BASE_URL = "https://perf.rust-lang.org/perf" ;
249
+
250
+ function post ( path , body ) {
251
+ return fetch ( BASE_URL + path , {
252
+ method : "POST" ,
253
+ // headers: {"Content-Type": "application/json"},
254
+ body : JSON . stringify ( body ) ,
255
+ } ) . then ( r => r . json ( ) ) ;
256
+ }
257
+
258
+ function prepData ( data ) {
259
+ let sortedBenchNames = Object . keys ( data . benchmarks ) . sort ( ) ;
260
+
261
+ let benchmarks = { } ;
262
+
263
+ function optInterpolated ( buildKind ) {
264
+ for ( let cacheState in buildKind )
265
+ buildKind [ cacheState ] . is_interpolated = new Set ( buildKind [ cacheState ] . is_interpolated ) ;
266
+
267
+ return buildKind ;
268
+ }
269
+
270
+ sortedBenchNames . forEach ( name => {
271
+ const { Check, Debug, Opt } = data . benchmarks [ name ] ;
272
+
273
+ benchmarks [ name ] = {
274
+ check : optInterpolated ( Check ) ,
275
+ debug : optInterpolated ( Debug ) ,
276
+ opt : optInterpolated ( Opt ) ,
277
+ }
278
+ } ) ;
279
+
280
+ data . benchmarks = benchmarks ;
281
+
282
+ return data ;
283
+ }
284
+
285
+ const state = {
286
+ start : "" ,
287
+ end : "" ,
288
+ stat : "instructions:u" ,
289
+ absolute : true ,
290
+ } ;
291
+
292
+ post ( "/graph-new" , state ) . then ( prepData ) . then ( data => renderPlots ( data , state ) ) ;
293
+ </ script >
294
+ </ body >
295
+ </ html >
0 commit comments