258 lines
6.3 KiB
Python
258 lines
6.3 KiB
Python
import numpy
|
|
import re
|
|
|
|
|
|
def IsFloat(text):
|
|
if text is None:
|
|
return False
|
|
try:
|
|
float(text)
|
|
return True
|
|
except ValueError:
|
|
return False
|
|
|
|
|
|
def RemoveTrailingZeros(x):
|
|
ret = x
|
|
ret = re.sub('\.0*$', '', ret)
|
|
ret = re.sub('(\.[1-9]*)0+$', '\\1', ret)
|
|
return ret
|
|
|
|
|
|
def HumanizeFloat(x, n=2):
|
|
if not IsFloat(x):
|
|
return x
|
|
digits = re.findall('[0-9.]', str(x))
|
|
decimal_found = False
|
|
ret = ''
|
|
sig_figs = 0
|
|
for digit in digits:
|
|
if digit == '.':
|
|
decimal_found = True
|
|
elif sig_figs != 0 or digit != '0':
|
|
sig_figs += 1
|
|
if decimal_found and sig_figs >= n:
|
|
break
|
|
ret += digit
|
|
return ret
|
|
|
|
|
|
def GetNSigFigs(x, n=2):
|
|
if not IsFloat(x):
|
|
return x
|
|
my_fmt = '%.' + str(n - 1) + 'e'
|
|
x_string = my_fmt % x
|
|
f = float(x_string)
|
|
return f
|
|
|
|
|
|
def GetFormattedPercent(baseline, other, bad_result='--'):
|
|
result = '%8s' % GetPercent(baseline, other, bad_result)
|
|
return result
|
|
|
|
|
|
def GetPercent(baseline, other, bad_result='--'):
|
|
result = bad_result
|
|
if IsFloat(baseline) and IsFloat(other):
|
|
try:
|
|
pct = (float(other) / float(baseline) - 1) * 100
|
|
result = '%+1.1f' % pct
|
|
except ZeroDivisionError:
|
|
pass
|
|
return result
|
|
|
|
|
|
def FitString(text, length):
|
|
if len(text) == length:
|
|
return text
|
|
elif len(text) > length:
|
|
return text[-length:]
|
|
else:
|
|
fmt = '%%%ds' % length
|
|
return fmt % text
|
|
|
|
|
|
class TableFormatter(object):
|
|
|
|
def __init__(self):
|
|
self.d = '\t'
|
|
self.bad_result = 'x'
|
|
|
|
def GetTablePercents(self, table):
|
|
# Assumes table is not transposed.
|
|
pct_table = []
|
|
|
|
pct_table.append(table[0])
|
|
for i in range(1, len(table)):
|
|
row = []
|
|
row.append(table[i][0])
|
|
for j in range(1, len(table[0])):
|
|
c = table[i][j]
|
|
b = table[i][1]
|
|
p = GetPercent(b, c, self.bad_result)
|
|
row.append(p)
|
|
pct_table.append(row)
|
|
return pct_table
|
|
|
|
def FormatFloat(self, c, max_length=8):
|
|
if not IsFloat(c):
|
|
return c
|
|
f = float(c)
|
|
ret = HumanizeFloat(f, 4)
|
|
ret = RemoveTrailingZeros(ret)
|
|
if len(ret) > max_length:
|
|
ret = '%1.1ef' % f
|
|
return ret
|
|
|
|
def TransposeTable(self, table):
|
|
transposed_table = []
|
|
for i in range(len(table[0])):
|
|
row = []
|
|
for j in range(len(table)):
|
|
row.append(table[j][i])
|
|
transposed_table.append(row)
|
|
return transposed_table
|
|
|
|
def GetTableLabels(self, table):
|
|
ret = ''
|
|
header = table[0]
|
|
for i in range(1, len(header)):
|
|
ret += '%d: %s\n' % (i, header[i])
|
|
return ret
|
|
|
|
def GetFormattedTable(self,
|
|
table,
|
|
transposed=False,
|
|
first_column_width=30,
|
|
column_width=14,
|
|
percents_only=True,
|
|
fit_string=True):
|
|
o = ''
|
|
pct_table = self.GetTablePercents(table)
|
|
if transposed == True:
|
|
table = self.TransposeTable(table)
|
|
pct_table = self.TransposeTable(table)
|
|
|
|
for i in range(0, len(table)):
|
|
for j in range(len(table[0])):
|
|
if j == 0:
|
|
width = first_column_width
|
|
else:
|
|
width = column_width
|
|
|
|
c = table[i][j]
|
|
p = pct_table[i][j]
|
|
|
|
# Replace labels with numbers: 0... n
|
|
if IsFloat(c):
|
|
c = self.FormatFloat(c)
|
|
|
|
if IsFloat(p) and not percents_only:
|
|
p = '%s%%' % p
|
|
|
|
# Print percent values side by side.
|
|
if j != 0:
|
|
if percents_only:
|
|
c = '%s' % p
|
|
else:
|
|
c = '%s (%s)' % (c, p)
|
|
|
|
if i == 0 and j != 0:
|
|
c = str(j)
|
|
|
|
if fit_string:
|
|
o += FitString(c, width) + self.d
|
|
else:
|
|
o += c + self.d
|
|
o += '\n'
|
|
return o
|
|
|
|
def GetGroups(self, table):
|
|
labels = table[0]
|
|
groups = []
|
|
group_dict = {}
|
|
for i in range(1, len(labels)):
|
|
label = labels[i]
|
|
stripped_label = self.GetStrippedLabel(label)
|
|
if stripped_label not in group_dict:
|
|
group_dict[stripped_label] = len(groups)
|
|
groups.append([])
|
|
groups[group_dict[stripped_label]].append(i)
|
|
return groups
|
|
|
|
def GetSummaryTableValues(self, table):
|
|
# First get the groups
|
|
groups = self.GetGroups(table)
|
|
|
|
summary_table = []
|
|
|
|
labels = table[0]
|
|
|
|
summary_labels = ['Summary Table']
|
|
for group in groups:
|
|
label = labels[group[0]]
|
|
stripped_label = self.GetStrippedLabel(label)
|
|
group_label = '%s (%d runs)' % (stripped_label, len(group))
|
|
summary_labels.append(group_label)
|
|
summary_table.append(summary_labels)
|
|
|
|
for i in range(1, len(table)):
|
|
row = table[i]
|
|
summary_row = [row[0]]
|
|
for group in groups:
|
|
group_runs = []
|
|
for index in group:
|
|
group_runs.append(row[index])
|
|
group_run = self.AggregateResults(group_runs)
|
|
summary_row.append(group_run)
|
|
summary_table.append(summary_row)
|
|
|
|
return summary_table
|
|
|
|
# Drop N% slowest and M% fastest numbers, and return arithmean of
|
|
# the remaining.
|
|
@staticmethod
|
|
def AverageWithDrops(numbers, slow_percent=20, fast_percent=20):
|
|
sorted_numbers = list(numbers)
|
|
sorted_numbers.sort()
|
|
num_slow = int(slow_percent / 100.0 * len(sorted_numbers))
|
|
num_fast = int(fast_percent / 100.0 * len(sorted_numbers))
|
|
sorted_numbers = sorted_numbers[num_slow:]
|
|
if num_fast:
|
|
sorted_numbers = sorted_numbers[:-num_fast]
|
|
return numpy.average(sorted_numbers)
|
|
|
|
@staticmethod
|
|
def AggregateResults(group_results):
|
|
ret = ''
|
|
if not group_results:
|
|
return ret
|
|
all_floats = True
|
|
all_passes = True
|
|
all_fails = True
|
|
for group_result in group_results:
|
|
if not IsFloat(group_result):
|
|
all_floats = False
|
|
if group_result != 'PASSED':
|
|
all_passes = False
|
|
if group_result != 'FAILED':
|
|
all_fails = False
|
|
if all_floats == True:
|
|
float_results = [float(v) for v in group_results]
|
|
ret = '%f' % TableFormatter.AverageWithDrops(float_results)
|
|
# Add this line for standard deviation.
|
|
### ret += " %f" % numpy.std(float_results)
|
|
elif all_passes == True:
|
|
ret = 'ALL_PASS'
|
|
elif all_fails == True:
|
|
ret = 'ALL_FAILS'
|
|
return ret
|
|
|
|
@staticmethod
|
|
def GetStrippedLabel(label):
|
|
return re.sub('\s*\S+:\S+\s*', '', label)
|
|
### return re.sub("\s*remote:\S*\s*i:\d+$", "", label)
|
|
|
|
@staticmethod
|
|
def GetLabelWithIteration(label, iteration):
|
|
return '%s i:%d' % (label, iteration)
|