Format.py 15 KB

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