Format.py 15 KB

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