1import math 2from datetime import date 3 4NORMALFORMAT=0 5BYCFORMAT=1 6BYDFORMAT=2 7 8def joinit(iterable, delimiter): 9 it = iter(iterable) 10 yield next(it) 11 for x in it: 12 yield delimiter 13 yield x 14 15# To format, in HTML, the cores in the right order. 16# First we order the categories 17# Then we order the cores in each category 18# The final ORDEREDCORES is what is used 19# to order tjhe values 20# Since some cores may be missing, each atble display 21# is computing a rstricted ordered core list with only the available cores. 22CORTEXCATEGORIES=["Cortex-M","Cortex-R","Cortex-A"] 23CORECATEGORIES={"Cortex-M":["m0","m4", "m7", "m33" , "m55 scalar", "m55 mve","m55 autovec"], 24"Cortex-R":["r8","r52"], 25"Cortex-A":["a32"] 26} 27ORDEREDCORES=[] 28for cat in CORTEXCATEGORIES: 29 cores=[] 30 if cat in CORECATEGORIES: 31 for core in CORECATEGORIES[cat]: 32 cores.append(core) 33 else: 34 print("Error core %s not found" % cat) 35 quit() 36 ORDEREDCORES += cores 37 38ORDEREDTYPES=["q7","q15","q31","u32","f16","f32","f64"] 39 40class Markdown: 41 def __init__(self,output): 42 self._id=0 43 self._output = output 44 45 def visitBarChart(self,data): 46 pass 47 48 def visitHistory(self,data): 49 pass 50 51 def visitText(self,text): 52 self._output.write(text) 53 54 # Write columns in markdown format 55 def writeColumns(self,cols): 56 colStr = "".join(joinit(cols,"|")) 57 self._output.write("|") 58 self._output.write(colStr) 59 self._output.write("|\n") 60 sepStr="".join(joinit([":-:" for x in cols],"|")) 61 self._output.write("|") 62 self._output.write(sepStr) 63 self._output.write("|\n") 64 65 # Write row in markdown format 66 def writeRow(self,row): 67 row=[str(x) for x in row] 68 rowStr = "".join(joinit(row,"|")) 69 self._output.write("|") 70 self._output.write(rowStr) 71 self._output.write("|\n") 72 73 def visitTable(self,table): 74 self.writeColumns(table.columns) 75 for row in table.rows: 76 self.writeRow(row) 77 78 def visitSection(self,section): 79 self._id = self._id + 1 80 header = "".join(["#" for i in range(self._id)]) 81 self._output.write("%s %s\n" % (header,section.name)) 82 83 def leaveSection(self,section): 84 self._id = self._id - 1 85 86 def visitDocument(self,document): 87 if document.runidHeader: 88 self._output.write("Document generated for run ids : %s\n" % document.runidHeader) 89 90 def leaveDocument(self,document): 91 pass 92 93styleSheet=""" 94<style type='text/css'> 95 96#TOC { 97 position: fixed; 98 left: 0; 99 top: 0; 100 width: 290px; 101 height: 100%; 102 overflow:auto; 103 margin-top:5px; 104 margin-bottom:10px; 105} 106 107html { 108 font-size: 16px; 109} 110 111html, body { 112 background-color: #E5ECEB; 113 font-family: "Lato"; 114 font-style: normal; font-variant: normal; 115 color: #002B49; 116 line-height: 1.5em; 117} 118 119body { 120 margin: auto; 121 margin-top:0px; 122 margin-left:290px; 123 124} 125 126.NA { 127 color: #999999; 128} 129 130.testname { 131 color: #0091BD; 132 font-size: 1.125em; 133} 134 135h1, 136h2, 137h3, 138h4, 139h5, 140h6 { 141 font-weight: bold; 142} 143h1 { 144 font-size: 1.875em; 145 margin-top:5px; 146} 147h2 { 148 font-size: 1.3125em; 149} 150h3 { 151 font-size: 1.3125em; 152 margin-left:1em; 153} 154h4 { 155 font-size: 1.125em; 156 margin-left:1em; 157} 158h5, 159h6 { 160 font-size: 1em; 161 margin-left:1em; 162} 163 164#TOC h1 { 165 margin-top:0em; 166 margin-left:0.5em; 167} 168 169table { 170 margin-bottom: 1.5em; 171 font-size: 1em; 172 width: 100%; 173 border-collapse: collapse; 174 border-spacing: 0; 175 width: 100%; 176 margin-left:1em; 177} 178thead th, 179tfoot th { 180 padding: .25em .25em .25em .4em; 181 text-transform: uppercase; 182} 183th { 184 text-align: left; 185} 186td { 187 vertical-align: top; 188 padding: .25em .25em .25em .4em; 189} 190 191.ty-table-edit { 192 background-color: transparent; 193} 194thead { 195 background-color: #979ea3; 196} 197tr:nth-child(even) { 198 background: #d7dadc; 199} 200 201ul, #myUL { 202 list-style-type: none; 203 padding-inline-start:10px; 204} 205 206 207 208/* Remove margins and padding from the parent ul */ 209#myUL { 210 margin: 0; 211 padding: 0; 212} 213 214/* Style the caret/arrow */ 215.caret { 216 cursor: pointer; 217 user-select: none; /* Prevent text selection */ 218} 219 220/* Create the caret/arrow with a unicode, and style it */ 221.caret::before { 222 content: "\\25B6"; 223 color: black; 224 display: inline-block; 225 margin-right: 6px; 226} 227 228/* Rotate the caret/arrow icon when clicked on (using JavaScript) */ 229.caret-down::before { 230 transform: rotate(90deg); 231} 232 233/* Hide the nested list */ 234.nested { 235 display: none; 236} 237 238/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */ 239.active { 240 display: block; 241} 242 243.firstcore { 244 border-left-color: black; 245 border-left-style: solid; 246 border-left-width: 2px; 247} 248 249 250</style> 251""" 252 253script="""<script type="text/javascript"> 254var toggler = document.getElementsByClassName("caret"); 255var i; 256for (i = 0; i < toggler.length; i++) { 257 toggler[i].addEventListener("click", function() { 258 this.parentElement.querySelector(".nested").classList.toggle("active"); 259 this.classList.toggle("caret-down"); 260 }); 261}</script>""" 262 263barscript=""" <script src="https://d3js.org/d3.v5.js"></script> 264 265 266 267<script type="text/javascript"> 268 269histwidth=400; 270histheight=200; 271histmargin={left:40,right:100,bottom:40,top:10}; 272 273function legend(color,svg) 274{ 275 const g = svg 276 .attr("transform", `translate(${histwidth},0)`) 277 .attr("text-anchor", "end") 278 .attr("font-family", "sans-serif") 279 .attr("font-size", 9) 280 .selectAll("g") 281 .data(color.domain().slice().reverse()) 282 .join("g") 283 .attr("transform", (d, i) => `translate(0,${i * 20})`); 284 285 g.append("rect") 286 .attr("x", -19) 287 .attr("width", 19) 288 .attr("height", 19) 289 .attr("fill", color); 290 291 g.append("text") 292 .attr("x", -24) 293 .attr("y", 9.5) 294 .attr("dy", "0.35em") 295 .text(d => d); 296 297} 298 299function myhist(data,theid) 300{ 301 var x,y,xAxis,yAxis,svg,color; 302 303 304 305color = d3.scaleOrdinal() 306 .domain(data.series.map(d => d['name'])) 307 .range(["#FF6B00", 308"#FFC700", 309"#95D600", 310"#00C1DE", 311"#0091BD", 312"#002B49", 313"#333E48", 314"#7D868C", 315"#E5ECEB"]); 316 317 svg = d3.select(theid).insert("svg") 318 .attr("viewBox", [0, 0, histwidth, histheight]); 319 320 321sx = d3.scaleLinear() 322 .domain(d3.extent(data.dates)) 323 .range([histmargin.left,histwidth - histmargin.right]); 324 325sy = d3.scaleLinear() 326 .domain([0, d3.max(data.series, d => d3.max(d.values,q => q[1]))]).nice() 327 .range([histheight - histmargin.bottom, histmargin.top]); 328 329xAxis = g => g 330 .attr("transform", `translate(0,${histheight - histmargin.bottom})`) 331 .call(d3.axisBottom(sx).tickValues(data.dates).ticks(histwidth / 80,"d"). 332 tickSizeOuter(0)); 333 334svg.append("text") 335 .attr("class", "x label") 336 .attr("text-anchor", "end") 337 .attr("x", histwidth/2.0) 338 .attr("y", histheight - 6) 339 .text("RUN ID"); 340 341yAxis = g => g 342 .attr("transform", `translate(${histmargin.left},0)`) 343 .call(d3.axisLeft(sy)) 344 .call(g => g.select(".domain").remove()); 345 346 347 348line = d3.line() 349 .x(d => sx(data.dates[d[0]]) 350 ) 351 .y(d => sy(d[1])); 352 353svg.append("g") 354 .call(xAxis); 355 356 svg.append("g") 357 .call(yAxis); 358 359 const path = svg.append("g") 360 .attr("fill", "none") 361 .attr("stroke", "steelblue") 362 .attr("stroke-width", 1.5) 363 .attr("stroke-linejoin", "round") 364 .attr("stroke-linecap", "round") 365 .selectAll("path") 366 .data(data.series) 367 .join("path") 368 .style("mix-blend-mode", "multiply") 369 .attr("stroke", d => color(d.name)) 370 .attr("d", d => line(d.values)); 371 372// Legend 373 374 375 376svg.append("g") 377 .call(d => legend(color,d)); 378 //svg.call(hover, path); 379 380 381} 382 383function mybar(data,theid) 384{ 385 var width,height,margin,x,y,xAxis,yAxis,svg,color; 386 387 width=400; 388 height=100; 389 margin={left:40,right:10,bottom:40,top:10}; 390 391 392 svg = d3.select(theid).insert("svg") 393 .attr("viewBox", [0, 0, width, height]);; 394 395x = d3.scaleBand() 396 .domain(d3.range(data.length)) 397 .range([margin.left, width - margin.right]) 398 .padding(0.1); 399 400y = d3.scaleLinear() 401 .domain([0, d3.max(data, d => d.value)]).nice() 402 .range([height - margin.bottom, margin.top]); 403 404xAxis = g => g 405 .attr("transform", `translate(0,${height - margin.bottom})`) 406 .call(d3.axisBottom(x).tickFormat(i => data[i].name).tickSizeOuter(0)); 407 408yAxis = g => g 409 .attr("transform", `translate(${margin.left},0)`) 410 .call(d3.axisLeft(y).ticks(4, data.format)) 411 .call(g => g.select(".domain").remove()) 412 .call(g => g.append("text") 413 .attr("x", -margin.left) 414 .attr("y", 10) 415 .attr("fill", "currentColor") 416 .attr("text-anchor", "start") 417 .text(data.y)); 418 419color = "steelblue" 420 421 svg.append("g") 422 .attr("fill", color) 423 .selectAll("rect") 424 .data(data) 425 .join("rect") 426 .attr("x", (d, i) => x(i)) 427 .attr("y", d => y(d.value)) 428 .attr("height", d => y(0) - y(d.value)) 429 .attr("width", x.bandwidth()); 430 431 svg.append("g") 432 .call(xAxis); 433 434 svg.append("g") 435 .call(yAxis); 436 437} 438</script>""" 439 440 441class HTMLToc: 442 def __init__(self,output): 443 self._id=0 444 self._sectionID = 0 445 self._output = output 446 447 448 449 def visitTable(self,table): 450 pass 451 452 def visitBarChart(self,data): 453 pass 454 455 def visitHistory(self,data): 456 pass 457 458 def visitText(self,text): 459 pass 460 461 def visitSection(self,section): 462 self._id = self._id + 1 463 self._sectionID = self._sectionID + 1 464 if section.hasChildren: 465 self._output.write("<li><span class=\"caret\"><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name)) 466 self._output.write("<ul class=\"nested\">\n") 467 else: 468 self._output.write("<li><span><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name)) 469 470 def leaveSection(self,section): 471 if section.hasChildren: 472 self._output.write("</ul></li>\n") 473 474 self._id = self._id - 1 475 476 def visitDocument(self,document): 477 self._output.write("<div id=\"TOC\"><h1>Table of content</h1><ul id=\"myUL\">\n") 478 479 480 def leaveDocument(self,document): 481 self._output.write("</ul></div>%s\n" % script) 482 483def permutation(ordered,unordered,mode): 484 result=[] 485 restricted=[] 486 order = ORDEREDCORES 487 if mode == BYDFORMAT: 488 order = ORDEREDTYPES 489 for c in order: 490 if c in unordered: 491 restricted.append(c) 492 493 for c in unordered: 494 result.append(restricted.index(c)) 495 496 return(result,restricted) 497 498def reorder(p,v): 499 result=[0 for x in v] 500 for val,i in zip(v,p): 501 result[i]=val 502 503 return(result) 504 505class HTML: 506 def __init__(self,output,regMode,ratio,reorder): 507 self._id=0 508 self._sectionID = 0 509 self._barID = 0 510 self._histID = 0 511 self._output = output 512 self._regMode = regMode 513 self._reorder = reorder 514 self._ratioMode = ratio and regMode 515 516 def visitBarChart(self,bar): 517 data=bar.data 518 datastr = "".join(joinit(["{name:'%s',value:%s}" % x for x in data],",")) 519 #print(datastr) 520 self._output.write("<p id=\"g%d\"></p>\n" % self._barID) 521 self._output.write("""<script type="text/javascript"> 522thedata%d=[%s]; 523mybar(thedata%d,"#g%d"); 524</script>""" % (self._barID,datastr,self._barID,self._barID)) 525 526 self._barID = self._barID + 1 527 528 def _getIndex(self,runids,data): 529 return([[runids.index(x[0]),x[1]] for x in data]) 530 531 def visitHistory(self,hist): 532 data=hist.data 533 runidstr = "".join(joinit([str(x) for x in hist.runids],",")) 534 serieelems=[] 535 for core in data: 536 serieelems.append("{name: '%s',values: %s}" % (core,self._getIndex(hist.runids,data[core]))) 537 538 seriestr = "".join(joinit(serieelems,",")) 539 datastr="""{ 540series: [%s], 541 dates: [%s] 542};""" %(seriestr,runidstr); 543 #print(datastr) 544 self._output.write("<p id=\"hi%d\"></p>\n" % self._histID) 545 self._output.write("""<script type="text/javascript"> 546thehdata%d=%s 547myhist(thehdata%d,"#hi%d"); 548</script>""" % (self._histID,datastr,self._histID,self._histID)) 549 550 self._histID = self._histID + 1 551 552 def visitText(self,text): 553 self._output.write("<p>\n") 554 self._output.write(text.text) 555 self._output.write("</p>\n") 556 557 def visitTable(self,table): 558 self._output.write("<table>\n") 559 self._output.write("<thead>\n") 560 self._output.write("<tr>\n") 561 firstCore = False 562 for col in table.params: 563 firstCore = True 564 self._output.write("<th class=\"param\">") 565 self._output.write(str(col)) 566 self._output.write("</th>\n") 567 568 if self._reorder == NORMALFORMAT: 569 perm,restricted=permutation(ORDEREDCORES,table.cores,self._reorder) 570 elif self._reorder == BYDFORMAT: 571 perm,restricted=permutation(ORDEREDTYPES,table.cores,self._reorder) 572 else: 573 restricted = table.cores 574 575 for col in restricted: 576 if firstCore: 577 self._output.write("<th class=\"firstcore\">") 578 else: 579 self._output.write("<th class=\"core\">") 580 self._output.write(str(col)) 581 self._output.write("</th>\n") 582 firstCore = False 583 self._output.write("</tr>\n") 584 self._output.write("</thead>\n") 585 586 nbParams = len(table.params) 587 for row in table.rows: 588 self._output.write("<tr>\n") 589 i = 0 590 591 row=list(row) 592 593 #print(row) 594 595 params=row[0:nbParams] 596 values=row[nbParams:] 597 598 if self._reorder == NORMALFORMAT: 599 row = params + reorder(perm,values) 600 elif self._reorder == BYDFORMAT: 601 row = params + reorder(perm,values) 602 else: 603 row = params + values 604 605 for elem in row: 606 txt=str(elem) 607 if txt == 'NA': 608 txt = "<span class=\"NA\">" + txt + "</span>" 609 if i < nbParams: 610 self._output.write("<td class=\"param\">") 611 self._output.write(txt) 612 self._output.write("</td>\n") 613 elif i == nbParams and nbParams != 0: 614 self._output.write("<td class=\"firstcore\">") 615 self._output.write(txt) 616 self._output.write("</td>\n") 617 else: 618 self._output.write("<td class=\"core\">") 619 self._output.write(txt) 620 self._output.write("</td>\n") 621 i = i + 1 622 self._output.write("</tr>\n") 623 self._output.write("</table>\n") 624 625 626 def visitSection(self,section): 627 self._id = self._id + 1 628 self._sectionID = self._sectionID + 1 629 name = section.name 630 if section.isTest: 631 name = "<span class=\"testname\">" + name + "</span>" 632 self._output.write("<h%d id=\"section%d\">%s</h%d>\n" % (self._id,self._sectionID,name,self._id)) 633 634 def leaveSection(self,section): 635 self._id = self._id - 1 636 637 def visitDocument(self,document): 638 self._output.write("""<!doctype html> 639<html> 640<head> 641<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'> 642<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato"> 643<title>Benchmarks</title>%s</head><body>\n""" % styleSheet) 644 if self._regMode and not self._ratioMode: 645 self._output.write("<h1>ECPS Benchmark Regressions</h1>\n") 646 elif self._ratioMode: 647 self._output.write("<h1>ECPS Benchmark Ratios</h1>\n") 648 else: 649 self._output.write("<h1>ECPS Benchmark Summary</h1>\n") 650 651 if document.runidHeader: 652 self._output.write("<p>Document generated for run ids : %s</p>\n" % document.runidHeader) 653 today = date.today() 654 d2 = today.strftime("%B %d, %Y") 655 self._output.write("<p>Document generated on %s</p>\n" % d2) 656 657 self._output.write(barscript) 658 659 def leaveDocument(self,document): 660 document.accept(HTMLToc(self._output)) 661 662 self._output.write("</body></html>\n") 663 664 665