You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
665 lines
15 KiB
Python
665 lines
15 KiB
Python
import math
|
|
from datetime import date
|
|
|
|
NORMALFORMAT=0
|
|
BYCFORMAT=1
|
|
BYDFORMAT=2
|
|
|
|
def joinit(iterable, delimiter):
|
|
it = iter(iterable)
|
|
yield next(it)
|
|
for x in it:
|
|
yield delimiter
|
|
yield x
|
|
|
|
# To format, in HTML, the cores in the right order.
|
|
# First we order the categories
|
|
# Then we order the cores in each category
|
|
# The final ORDEREDCORES is what is used
|
|
# to order tjhe values
|
|
# Since some cores may be missing, each atble display
|
|
# is computing a rstricted ordered core list with only the available cores.
|
|
CORTEXCATEGORIES=["Cortex-M","Cortex-R","Cortex-A"]
|
|
CORECATEGORIES={"Cortex-M":["m0","m4", "m7", "m33" , "m55 scalar", "m55 mve","m55 autovec"],
|
|
"Cortex-R":["r8","r52"],
|
|
"Cortex-A":["a32"]
|
|
}
|
|
ORDEREDCORES=[]
|
|
for cat in CORTEXCATEGORIES:
|
|
cores=[]
|
|
if cat in CORECATEGORIES:
|
|
for core in CORECATEGORIES[cat]:
|
|
cores.append(core)
|
|
else:
|
|
print("Error core %s not found" % cat)
|
|
quit()
|
|
ORDEREDCORES += cores
|
|
|
|
ORDEREDTYPES=["q7","q15","q31","u32","f16","f32","f64"]
|
|
|
|
class Markdown:
|
|
def __init__(self,output):
|
|
self._id=0
|
|
self._output = output
|
|
|
|
def visitBarChart(self,data):
|
|
pass
|
|
|
|
def visitHistory(self,data):
|
|
pass
|
|
|
|
def visitText(self,text):
|
|
self._output.write(text)
|
|
|
|
# Write columns in markdown format
|
|
def writeColumns(self,cols):
|
|
colStr = "".join(joinit(cols,"|"))
|
|
self._output.write("|")
|
|
self._output.write(colStr)
|
|
self._output.write("|\n")
|
|
sepStr="".join(joinit([":-:" for x in cols],"|"))
|
|
self._output.write("|")
|
|
self._output.write(sepStr)
|
|
self._output.write("|\n")
|
|
|
|
# Write row in markdown format
|
|
def writeRow(self,row):
|
|
row=[str(x) for x in row]
|
|
rowStr = "".join(joinit(row,"|"))
|
|
self._output.write("|")
|
|
self._output.write(rowStr)
|
|
self._output.write("|\n")
|
|
|
|
def visitTable(self,table):
|
|
self.writeColumns(table.columns)
|
|
for row in table.rows:
|
|
self.writeRow(row)
|
|
|
|
def visitSection(self,section):
|
|
self._id = self._id + 1
|
|
header = "".join(["#" for i in range(self._id)])
|
|
self._output.write("%s %s\n" % (header,section.name))
|
|
|
|
def leaveSection(self,section):
|
|
self._id = self._id - 1
|
|
|
|
def visitDocument(self,document):
|
|
if document.runidHeader:
|
|
self._output.write("Document generated for run ids : %s\n" % document.runidHeader)
|
|
|
|
def leaveDocument(self,document):
|
|
pass
|
|
|
|
styleSheet="""
|
|
<style type='text/css'>
|
|
|
|
#TOC {
|
|
position: fixed;
|
|
left: 0;
|
|
top: 0;
|
|
width: 290px;
|
|
height: 100%;
|
|
overflow:auto;
|
|
margin-top:5px;
|
|
margin-bottom:10px;
|
|
}
|
|
|
|
html {
|
|
font-size: 16px;
|
|
}
|
|
|
|
html, body {
|
|
background-color: #E5ECEB;
|
|
font-family: "Lato";
|
|
font-style: normal; font-variant: normal;
|
|
color: #002B49;
|
|
line-height: 1.5em;
|
|
}
|
|
|
|
body {
|
|
margin: auto;
|
|
margin-top:0px;
|
|
margin-left:290px;
|
|
|
|
}
|
|
|
|
.NA {
|
|
color: #999999;
|
|
}
|
|
|
|
.testname {
|
|
color: #0091BD;
|
|
font-size: 1.125em;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
font-weight: bold;
|
|
}
|
|
h1 {
|
|
font-size: 1.875em;
|
|
margin-top:5px;
|
|
}
|
|
h2 {
|
|
font-size: 1.3125em;
|
|
}
|
|
h3 {
|
|
font-size: 1.3125em;
|
|
margin-left:1em;
|
|
}
|
|
h4 {
|
|
font-size: 1.125em;
|
|
margin-left:1em;
|
|
}
|
|
h5,
|
|
h6 {
|
|
font-size: 1em;
|
|
margin-left:1em;
|
|
}
|
|
|
|
#TOC h1 {
|
|
margin-top:0em;
|
|
margin-left:0.5em;
|
|
}
|
|
|
|
table {
|
|
margin-bottom: 1.5em;
|
|
font-size: 1em;
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
border-spacing: 0;
|
|
width: 100%;
|
|
margin-left:1em;
|
|
}
|
|
thead th,
|
|
tfoot th {
|
|
padding: .25em .25em .25em .4em;
|
|
text-transform: uppercase;
|
|
}
|
|
th {
|
|
text-align: left;
|
|
}
|
|
td {
|
|
vertical-align: top;
|
|
padding: .25em .25em .25em .4em;
|
|
}
|
|
|
|
.ty-table-edit {
|
|
background-color: transparent;
|
|
}
|
|
thead {
|
|
background-color: #979ea3;
|
|
}
|
|
tr:nth-child(even) {
|
|
background: #d7dadc;
|
|
}
|
|
|
|
ul, #myUL {
|
|
list-style-type: none;
|
|
padding-inline-start:10px;
|
|
}
|
|
|
|
|
|
|
|
/* Remove margins and padding from the parent ul */
|
|
#myUL {
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
/* Style the caret/arrow */
|
|
.caret {
|
|
cursor: pointer;
|
|
user-select: none; /* Prevent text selection */
|
|
}
|
|
|
|
/* Create the caret/arrow with a unicode, and style it */
|
|
.caret::before {
|
|
content: "\\25B6";
|
|
color: black;
|
|
display: inline-block;
|
|
margin-right: 6px;
|
|
}
|
|
|
|
/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
|
|
.caret-down::before {
|
|
transform: rotate(90deg);
|
|
}
|
|
|
|
/* Hide the nested list */
|
|
.nested {
|
|
display: none;
|
|
}
|
|
|
|
/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
|
|
.active {
|
|
display: block;
|
|
}
|
|
|
|
.firstcore {
|
|
border-left-color: black;
|
|
border-left-style: solid;
|
|
border-left-width: 2px;
|
|
}
|
|
|
|
|
|
</style>
|
|
"""
|
|
|
|
script="""<script type="text/javascript">
|
|
var toggler = document.getElementsByClassName("caret");
|
|
var i;
|
|
for (i = 0; i < toggler.length; i++) {
|
|
toggler[i].addEventListener("click", function() {
|
|
this.parentElement.querySelector(".nested").classList.toggle("active");
|
|
this.classList.toggle("caret-down");
|
|
});
|
|
}</script>"""
|
|
|
|
barscript=""" <script src="https://d3js.org/d3.v5.js"></script>
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
histwidth=400;
|
|
histheight=200;
|
|
histmargin={left:40,right:100,bottom:40,top:10};
|
|
|
|
function legend(color,svg)
|
|
{
|
|
const g = svg
|
|
.attr("transform", `translate(${histwidth},0)`)
|
|
.attr("text-anchor", "end")
|
|
.attr("font-family", "sans-serif")
|
|
.attr("font-size", 9)
|
|
.selectAll("g")
|
|
.data(color.domain().slice().reverse())
|
|
.join("g")
|
|
.attr("transform", (d, i) => `translate(0,${i * 20})`);
|
|
|
|
g.append("rect")
|
|
.attr("x", -19)
|
|
.attr("width", 19)
|
|
.attr("height", 19)
|
|
.attr("fill", color);
|
|
|
|
g.append("text")
|
|
.attr("x", -24)
|
|
.attr("y", 9.5)
|
|
.attr("dy", "0.35em")
|
|
.text(d => d);
|
|
|
|
}
|
|
|
|
function myhist(data,theid)
|
|
{
|
|
var x,y,xAxis,yAxis,svg,color;
|
|
|
|
|
|
|
|
color = d3.scaleOrdinal()
|
|
.domain(data.series.map(d => d['name']))
|
|
.range(["#FF6B00",
|
|
"#FFC700",
|
|
"#95D600",
|
|
"#00C1DE",
|
|
"#0091BD",
|
|
"#002B49",
|
|
"#333E48",
|
|
"#7D868C",
|
|
"#E5ECEB"]);
|
|
|
|
svg = d3.select(theid).insert("svg")
|
|
.attr("viewBox", [0, 0, histwidth, histheight]);
|
|
|
|
|
|
sx = d3.scaleLinear()
|
|
.domain(d3.extent(data.dates))
|
|
.range([histmargin.left,histwidth - histmargin.right]);
|
|
|
|
sy = d3.scaleLinear()
|
|
.domain([0, d3.max(data.series, d => d3.max(d.values,q => q[1]))]).nice()
|
|
.range([histheight - histmargin.bottom, histmargin.top]);
|
|
|
|
xAxis = g => g
|
|
.attr("transform", `translate(0,${histheight - histmargin.bottom})`)
|
|
.call(d3.axisBottom(sx).tickValues(data.dates).ticks(histwidth / 80,"d").
|
|
tickSizeOuter(0));
|
|
|
|
svg.append("text")
|
|
.attr("class", "x label")
|
|
.attr("text-anchor", "end")
|
|
.attr("x", histwidth/2.0)
|
|
.attr("y", histheight - 6)
|
|
.text("RUN ID");
|
|
|
|
yAxis = g => g
|
|
.attr("transform", `translate(${histmargin.left},0)`)
|
|
.call(d3.axisLeft(sy))
|
|
.call(g => g.select(".domain").remove());
|
|
|
|
|
|
|
|
line = d3.line()
|
|
.x(d => sx(data.dates[d[0]])
|
|
)
|
|
.y(d => sy(d[1]));
|
|
|
|
svg.append("g")
|
|
.call(xAxis);
|
|
|
|
svg.append("g")
|
|
.call(yAxis);
|
|
|
|
const path = svg.append("g")
|
|
.attr("fill", "none")
|
|
.attr("stroke", "steelblue")
|
|
.attr("stroke-width", 1.5)
|
|
.attr("stroke-linejoin", "round")
|
|
.attr("stroke-linecap", "round")
|
|
.selectAll("path")
|
|
.data(data.series)
|
|
.join("path")
|
|
.style("mix-blend-mode", "multiply")
|
|
.attr("stroke", d => color(d.name))
|
|
.attr("d", d => line(d.values));
|
|
|
|
// Legend
|
|
|
|
|
|
|
|
svg.append("g")
|
|
.call(d => legend(color,d));
|
|
//svg.call(hover, path);
|
|
|
|
|
|
}
|
|
|
|
function mybar(data,theid)
|
|
{
|
|
var width,height,margin,x,y,xAxis,yAxis,svg,color;
|
|
|
|
width=400;
|
|
height=100;
|
|
margin={left:40,right:10,bottom:40,top:10};
|
|
|
|
|
|
svg = d3.select(theid).insert("svg")
|
|
.attr("viewBox", [0, 0, width, height]);;
|
|
|
|
x = d3.scaleBand()
|
|
.domain(d3.range(data.length))
|
|
.range([margin.left, width - margin.right])
|
|
.padding(0.1);
|
|
|
|
y = d3.scaleLinear()
|
|
.domain([0, d3.max(data, d => d.value)]).nice()
|
|
.range([height - margin.bottom, margin.top]);
|
|
|
|
xAxis = g => g
|
|
.attr("transform", `translate(0,${height - margin.bottom})`)
|
|
.call(d3.axisBottom(x).tickFormat(i => data[i].name).tickSizeOuter(0));
|
|
|
|
yAxis = g => g
|
|
.attr("transform", `translate(${margin.left},0)`)
|
|
.call(d3.axisLeft(y).ticks(4, data.format))
|
|
.call(g => g.select(".domain").remove())
|
|
.call(g => g.append("text")
|
|
.attr("x", -margin.left)
|
|
.attr("y", 10)
|
|
.attr("fill", "currentColor")
|
|
.attr("text-anchor", "start")
|
|
.text(data.y));
|
|
|
|
color = "steelblue"
|
|
|
|
svg.append("g")
|
|
.attr("fill", color)
|
|
.selectAll("rect")
|
|
.data(data)
|
|
.join("rect")
|
|
.attr("x", (d, i) => x(i))
|
|
.attr("y", d => y(d.value))
|
|
.attr("height", d => y(0) - y(d.value))
|
|
.attr("width", x.bandwidth());
|
|
|
|
svg.append("g")
|
|
.call(xAxis);
|
|
|
|
svg.append("g")
|
|
.call(yAxis);
|
|
|
|
}
|
|
</script>"""
|
|
|
|
|
|
class HTMLToc:
|
|
def __init__(self,output):
|
|
self._id=0
|
|
self._sectionID = 0
|
|
self._output = output
|
|
|
|
|
|
|
|
def visitTable(self,table):
|
|
pass
|
|
|
|
def visitBarChart(self,data):
|
|
pass
|
|
|
|
def visitHistory(self,data):
|
|
pass
|
|
|
|
def visitText(self,text):
|
|
pass
|
|
|
|
def visitSection(self,section):
|
|
self._id = self._id + 1
|
|
self._sectionID = self._sectionID + 1
|
|
if section.hasChildren:
|
|
self._output.write("<li><span class=\"caret\"><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name))
|
|
self._output.write("<ul class=\"nested\">\n")
|
|
else:
|
|
self._output.write("<li><span><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name))
|
|
|
|
def leaveSection(self,section):
|
|
if section.hasChildren:
|
|
self._output.write("</ul></li>\n")
|
|
|
|
self._id = self._id - 1
|
|
|
|
def visitDocument(self,document):
|
|
self._output.write("<div id=\"TOC\"><h1>Table of content</h1><ul id=\"myUL\">\n")
|
|
|
|
|
|
def leaveDocument(self,document):
|
|
self._output.write("</ul></div>%s\n" % script)
|
|
|
|
def permutation(ordered,unordered,mode):
|
|
result=[]
|
|
restricted=[]
|
|
order = ORDEREDCORES
|
|
if mode == BYDFORMAT:
|
|
order = ORDEREDTYPES
|
|
for c in order:
|
|
if c in unordered:
|
|
restricted.append(c)
|
|
|
|
for c in unordered:
|
|
result.append(restricted.index(c))
|
|
|
|
return(result,restricted)
|
|
|
|
def reorder(p,v):
|
|
result=[0 for x in v]
|
|
for val,i in zip(v,p):
|
|
result[i]=val
|
|
|
|
return(result)
|
|
|
|
class HTML:
|
|
def __init__(self,output,regMode,ratio,reorder):
|
|
self._id=0
|
|
self._sectionID = 0
|
|
self._barID = 0
|
|
self._histID = 0
|
|
self._output = output
|
|
self._regMode = regMode
|
|
self._reorder = reorder
|
|
self._ratioMode = ratio and regMode
|
|
|
|
def visitBarChart(self,bar):
|
|
data=bar.data
|
|
datastr = "".join(joinit(["{name:'%s',value:%s}" % x for x in data],","))
|
|
#print(datastr)
|
|
self._output.write("<p id=\"g%d\"></p>\n" % self._barID)
|
|
self._output.write("""<script type="text/javascript">
|
|
thedata%d=[%s];
|
|
mybar(thedata%d,"#g%d");
|
|
</script>""" % (self._barID,datastr,self._barID,self._barID))
|
|
|
|
self._barID = self._barID + 1
|
|
|
|
def _getIndex(self,runids,data):
|
|
return([[runids.index(x[0]),x[1]] for x in data])
|
|
|
|
def visitHistory(self,hist):
|
|
data=hist.data
|
|
runidstr = "".join(joinit([str(x) for x in hist.runids],","))
|
|
serieelems=[]
|
|
for core in data:
|
|
serieelems.append("{name: '%s',values: %s}" % (core,self._getIndex(hist.runids,data[core])))
|
|
|
|
seriestr = "".join(joinit(serieelems,","))
|
|
datastr="""{
|
|
series: [%s],
|
|
dates: [%s]
|
|
};""" %(seriestr,runidstr);
|
|
#print(datastr)
|
|
self._output.write("<p id=\"hi%d\"></p>\n" % self._histID)
|
|
self._output.write("""<script type="text/javascript">
|
|
thehdata%d=%s
|
|
myhist(thehdata%d,"#hi%d");
|
|
</script>""" % (self._histID,datastr,self._histID,self._histID))
|
|
|
|
self._histID = self._histID + 1
|
|
|
|
def visitText(self,text):
|
|
self._output.write("<p>\n")
|
|
self._output.write(text.text)
|
|
self._output.write("</p>\n")
|
|
|
|
def visitTable(self,table):
|
|
self._output.write("<table>\n")
|
|
self._output.write("<thead>\n")
|
|
self._output.write("<tr>\n")
|
|
firstCore = False
|
|
for col in table.params:
|
|
firstCore = True
|
|
self._output.write("<th class=\"param\">")
|
|
self._output.write(str(col))
|
|
self._output.write("</th>\n")
|
|
|
|
if self._reorder == NORMALFORMAT:
|
|
perm,restricted=permutation(ORDEREDCORES,table.cores,self._reorder)
|
|
elif self._reorder == BYDFORMAT:
|
|
perm,restricted=permutation(ORDEREDTYPES,table.cores,self._reorder)
|
|
else:
|
|
restricted = table.cores
|
|
|
|
for col in restricted:
|
|
if firstCore:
|
|
self._output.write("<th class=\"firstcore\">")
|
|
else:
|
|
self._output.write("<th class=\"core\">")
|
|
self._output.write(str(col))
|
|
self._output.write("</th>\n")
|
|
firstCore = False
|
|
self._output.write("</tr>\n")
|
|
self._output.write("</thead>\n")
|
|
|
|
nbParams = len(table.params)
|
|
for row in table.rows:
|
|
self._output.write("<tr>\n")
|
|
i = 0
|
|
|
|
row=list(row)
|
|
|
|
#print(row)
|
|
|
|
params=row[0:nbParams]
|
|
values=row[nbParams:]
|
|
|
|
if self._reorder == NORMALFORMAT:
|
|
row = params + reorder(perm,values)
|
|
elif self._reorder == BYDFORMAT:
|
|
row = params + reorder(perm,values)
|
|
else:
|
|
row = params + values
|
|
|
|
for elem in row:
|
|
txt=str(elem)
|
|
if txt == 'NA':
|
|
txt = "<span class=\"NA\">" + txt + "</span>"
|
|
if i < nbParams:
|
|
self._output.write("<td class=\"param\">")
|
|
self._output.write(txt)
|
|
self._output.write("</td>\n")
|
|
elif i == nbParams and nbParams != 0:
|
|
self._output.write("<td class=\"firstcore\">")
|
|
self._output.write(txt)
|
|
self._output.write("</td>\n")
|
|
else:
|
|
self._output.write("<td class=\"core\">")
|
|
self._output.write(txt)
|
|
self._output.write("</td>\n")
|
|
i = i + 1
|
|
self._output.write("</tr>\n")
|
|
self._output.write("</table>\n")
|
|
|
|
|
|
def visitSection(self,section):
|
|
self._id = self._id + 1
|
|
self._sectionID = self._sectionID + 1
|
|
name = section.name
|
|
if section.isTest:
|
|
name = "<span class=\"testname\">" + name + "</span>"
|
|
self._output.write("<h%d id=\"section%d\">%s</h%d>\n" % (self._id,self._sectionID,name,self._id))
|
|
|
|
def leaveSection(self,section):
|
|
self._id = self._id - 1
|
|
|
|
def visitDocument(self,document):
|
|
self._output.write("""<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
|
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato">
|
|
<title>Benchmarks</title>%s</head><body>\n""" % styleSheet)
|
|
if self._regMode and not self._ratioMode:
|
|
self._output.write("<h1>ECPS Benchmark Regressions</h1>\n")
|
|
elif self._ratioMode:
|
|
self._output.write("<h1>ECPS Benchmark Ratios</h1>\n")
|
|
else:
|
|
self._output.write("<h1>ECPS Benchmark Summary</h1>\n")
|
|
|
|
if document.runidHeader:
|
|
self._output.write("<p>Document generated for run ids : %s</p>\n" % document.runidHeader)
|
|
today = date.today()
|
|
d2 = today.strftime("%B %d, %Y")
|
|
self._output.write("<p>Document generated on %s</p>\n" % d2)
|
|
|
|
self._output.write(barscript)
|
|
|
|
def leaveDocument(self,document):
|
|
document.accept(HTMLToc(self._output))
|
|
|
|
self._output.write("</body></html>\n")
|
|
|
|
|