[shell-perf-web] Improve formatting of displayed values
- From: Owen Taylor <otaylor src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shell-perf-web] Improve formatting of displayed values
- Date: Wed, 26 May 2010 22:42:35 +0000 (UTC)
commit 9e9933b07952a6032de78e5e8daccbe7f1e8ba11
Author: Owen W. Taylor <otaylor fishsoup net>
Date: Wed May 26 18:40:10 2010 -0400
Improve formatting of displayed values
For each displayed metric in a row of an output table, use the
actual metric values, the difference between the different values
in the row, and the supplied units to come up with an appropriate
number of significant figures.
For seconds and bytes, pick between us/ms/s and B/KiB/MiB based on
the values, not based upon the raw suppplied units.
shell/perf/report_table.py | 154 +++++++++++++++++++++++++++++++++-
templates/include/report_table.html | 2 +-
2 files changed, 150 insertions(+), 6 deletions(-)
---
diff --git a/shell/perf/report_table.py b/shell/perf/report_table.py
index a55ca90..fb5500a 100644
--- a/shell/perf/report_table.py
+++ b/shell/perf/report_table.py
@@ -1,6 +1,107 @@
from models import Metric
+import math
import sys
+BYTE_UNITS = {
+ 'B': 1,
+ 'KiB': 1024,
+ 'MiB': 1024*1024
+}
+
+TIME_UNITS = {
+ 's': 1,
+ 'ms': 0.001,
+ 'us': 0.000001
+}
+
+def format_values(values, units):
+ # This routine figures out how to display the values on a line
+ # of the report using three pieces of information
+ #
+ # - The absolute magnitude of the values
+ # - The difference between the values
+ # - The units for the given metric
+ #
+ # I've marked various arbitrary parameters below as TWEAKABLE
+
+ if len(values) == 0:
+ return []
+
+ high = max(max(values), - min(values))
+ suffix = ''
+
+ if units in BYTE_UNITS:
+ mult = BYTE_UNITS[units]
+
+ # If we have a difference of only a few bytes, then
+ # we don't want to display fractional K/M with a lot
+ # of precision
+ diff = mult * (max(values) - min(values))
+ if diff == 0 or diff > 100:
+
+ # TWEAKABLE: transition points from B => K => M
+ if high * mult >= 1024 * 1024:
+ mult = mult / (1024. * 1024.)
+ suffix = 'M'
+ units = 'MiB'
+ elif high * mult >= 1024:
+ mult = mult / 1024.
+ suffix = 'K'
+ units = 'KiB'
+ else:
+ units = 'B'
+
+ high = high * mult
+ values = [ mult * v for v in values ]
+
+ elif units in TIME_UNITS:
+ mult = TIME_UNITS[units]
+
+ # TWEAKABLE: transition points from us => s => s
+ if high * mult >= 0.1:
+ units = suffix = 's'
+ elif high * mult >= 0.0001:
+ units = suffix = 'ms'
+ mult *= 1000
+ else:
+ units = suffix = 'us'
+ mult *= 1000000
+
+ high = high * mult
+ values = [ mult * v for v in values ]
+
+ # Determine how many digits we need to avoid scientific notation
+ if high == 0:
+ digits = 1
+ else:
+ digits = 1 + math.floor(math.log10(high))
+ # TWEAKABLE: minimum number of displayed significant digits
+ # before we switch to scientific notation
+ digits = max(digits, 3)
+ # TWEAKABLE: maximum number of displayed significant digits
+ # before we switch to scientific notation
+ digits = min(digits, 6)
+
+ # If we have multiple values, determine how many digits we need
+ # to distinguish the values
+ if len(values) > 1:
+ diff = max(values) - min(values)
+ if diff > 0:
+ diff_digits = 1 + math.floor(math.log10(high)) - math.floor(math.log10(diff))
+ digits = max(digits, diff_digits)
+
+ digits = int(digits)
+
+ format = '%.*g' + suffix
+ result = []
+ for v in values:
+ if v is None:
+ result.append("")
+ else:
+ result.append(format % (digits, v))
+
+ return result, units
+
class ReportTable:
def __init__(self):
self.reports = []
@@ -39,10 +140,12 @@ class ReportTable:
m = report_metrics[metric.name]
values.append(m.value)
else:
- values.append(None)
+ values.append(m.value)
+ formatted, units = format_values(values, metric.units)
self.__rows.append({ 'metric': metric,
- 'values': values })
+ 'units': units,
+ 'values': formatted })
@property
def rows(self):
@@ -81,10 +184,11 @@ class RunTable:
metric = metrics[name]
# For ReportTable, row.metric is a Metric object, here it's just a dictionary
# that looks much the same to to the template
+ formatted, units = format_values(metric['values'], metric['units'])
self.__rows.append({ 'metric': { 'name': name,
- 'description': metric['description'],
- 'units': metric['units'] },
- 'values': metric['values'] })
+ 'description': metric['description'] },
+ 'units': units,
+ 'values': formatted })
@property
def col_headers(self):
@@ -95,3 +199,43 @@ class RunTable:
def rows(self):
self.__get_data()
return self.__rows
+
+if __name__ == '__main__':
+ def test(values, units, expected):
+ results, new_units = format_values(values, units)
+ if results != expected:
+ raise AssertionError('Formatting %r (%s), expected %r, got %r' %
+ (values, units, expected, results))
+
+ # 0 length list
+ test([], '', [])
+
+ # Test points at which we give up and use scientific notation
+ test([0.00001], '', ['1e-05'])
+ test([0.0001], '', ['0.0001'])
+ test([100000], '', ['100000'])
+ test([1000000], '', ['1e+06'])
+
+ # Check points at which we switch between us/ms/s
+ test([90], 'us', ['90us'])
+ test([110], 'us', ['0.11ms'])
+ test([90], 'ms', ['90ms'])
+ test([110], 'ms', ['0.11s'])
+
+ # Check seconds converting to something else
+ test([0.09], 's', ['90ms'])
+
+ # Check points at which we switch between B/KB/MB
+ test([1023], 'B', ['1023'])
+ test([1024], 'B', ['1K'])
+ test([1024*1024 - 1], 'B', ['1024K'])
+ test([1024*1024], 'B', ['1M'])
+
+ # Check KB => B, MB => KB
+ test([0.1], 'KiB', ['102'])
+ test([0.1], 'MiB', ['102K'])
+
+ test([1100000,1200000], '', ['1.1e+06', '1.2e+06'])
+ test([1000001,1000002], '', ['1000001', '1000002'])
+ test([1.10000,1.20000], '', ['1.1', '1.2'])
+ test([1.00001,1.00002], '', ['1.00001', '1.00002'])
diff --git a/templates/include/report_table.html b/templates/include/report_table.html
index 6913acc..d972bf7 100644
--- a/templates/include/report_table.html
+++ b/templates/include/report_table.html
@@ -15,7 +15,7 @@
<tbody>
{% for row in report_table.rows %}
<tr class="metric-row metric-row-{% cycle 'odd' 'even' %}">
- <th title="{{ row.metric.description }} ({{row.metric.units}})">{{ row.metric.name }}</th>
+ <th title="{{ row.metric.description }} ({{row.units}})">{{ row.metric.name }}</th>
{% for v in row.values %}
<td>{{ v }}</td>
{% endfor %}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]