lkml.org 
[lkml]   [2022]   [Dec]   [21]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
Subject[PATCH v2 2/9] perf jevents metric: Add ability to rewrite metrics in terms of others
From
Add RewriteMetricsInTermsOfOthers that iterates over pairs of names
and expressions trying to replace an expression, within the current
expression, with its name.

Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/pmu-events/metric.py | 73 +++++++++++++++++++++++++++-
tools/perf/pmu-events/metric_test.py | 10 ++++
2 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/tools/perf/pmu-events/metric.py b/tools/perf/pmu-events/metric.py
index 2f2fd220e843..ed13efac7389 100644
--- a/tools/perf/pmu-events/metric.py
+++ b/tools/perf/pmu-events/metric.py
@@ -4,7 +4,7 @@ import ast
import decimal
import json
import re
-from typing import Dict, List, Optional, Set, Union
+from typing import Dict, List, Optional, Set, Tuple, Union


class Expression:
@@ -26,6 +26,9 @@ class Expression:
"""Returns true when two expressions are the same."""
raise NotImplementedError()

+ def Substitute(self, name: str, expression: 'Expression') -> 'Expression':
+ raise NotImplementedError()
+
def __str__(self) -> str:
return self.ToPerfJson()

@@ -186,6 +189,15 @@ class Operator(Expression):
other.lhs) and self.rhs.Equals(other.rhs)
return False

+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ if self.Equals(expression):
+ return Event(name)
+ lhs = self.lhs.Substitute(name, expression)
+ rhs = None
+ if self.rhs:
+ rhs = self.rhs.Substitute(name, expression)
+ return Operator(self.operator, lhs, rhs)
+

class Select(Expression):
"""Represents a select ternary in the parse tree."""
@@ -225,6 +237,14 @@ class Select(Expression):
other.false_val) and self.true_val.Equals(other.true_val)
return False

+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ if self.Equals(expression):
+ return Event(name)
+ true_val = self.true_val.Substitute(name, expression)
+ cond = self.cond.Substitute(name, expression)
+ false_val = self.false_val.Substitute(name, expression)
+ return Select(true_val, cond, false_val)
+

class Function(Expression):
"""A function in an expression like min, max, d_ratio."""
@@ -267,6 +287,15 @@ class Function(Expression):
return result
return False

+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ if self.Equals(expression):
+ return Event(name)
+ lhs = self.lhs.Substitute(name, expression)
+ rhs = None
+ if self.rhs:
+ rhs = self.rhs.Substitute(name, expression)
+ return Function(self.fn, lhs, rhs)
+

def _FixEscapes(s: str) -> str:
s = re.sub(r'([^\\]),', r'\1\\,', s)
@@ -293,6 +322,9 @@ class Event(Expression):
def Equals(self, other: Expression) -> bool:
return isinstance(other, Event) and self.name == other.name

+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ return self
+

class Constant(Expression):
"""A constant within the expression tree."""
@@ -317,6 +349,9 @@ class Constant(Expression):
def Equals(self, other: Expression) -> bool:
return isinstance(other, Constant) and self.value == other.value

+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ return self
+

class Literal(Expression):
"""A runtime literal within the expression tree."""
@@ -336,6 +371,9 @@ class Literal(Expression):
def Equals(self, other: Expression) -> bool:
return isinstance(other, Literal) and self.value == other.value

+ def Substitute(self, name: str, expression: Expression) -> Expression:
+ return self
+

def min(lhs: Union[int, float, Expression], rhs: Union[int, float,
Expression]) -> Function:
@@ -461,6 +499,7 @@ class MetricGroup:


class _RewriteIfExpToSelect(ast.NodeTransformer):
+ """Transformer to convert if-else nodes to Select expressions."""

def visit_IfExp(self, node):
# pylint: disable=invalid-name
@@ -498,7 +537,37 @@ def ParsePerfJson(orig: str) -> Expression:
for kw in keywords:
py = re.sub(rf'Event\(r"{kw}"\)', kw, py)

- parsed = ast.parse(py, mode='eval')
+ try:
+ parsed = ast.parse(py, mode='eval')
+ except SyntaxError as e:
+ raise SyntaxError(f'Parsing expression:\n{orig}') from e
_RewriteIfExpToSelect().visit(parsed)
parsed = ast.fix_missing_locations(parsed)
return _Constify(eval(compile(parsed, orig, 'eval')))
+
+
+def RewriteMetricsInTermsOfOthers(metrics: list[Tuple[str, Expression]]
+ )-> Dict[str, Expression]:
+ """Shorten metrics by rewriting in terms of others.
+
+ Args:
+ metrics (list): pairs of metric names and their expressions.
+ Returns:
+ Dict: mapping from a metric name to a shortened expression.
+ """
+ updates: Dict[str, Expression] = dict()
+ for outer_name, outer_expression in metrics:
+ updated = outer_expression
+ while True:
+ for inner_name, inner_expression in metrics:
+ if inner_name.lower() == outer_name.lower():
+ continue
+ if inner_name in updates:
+ inner_expression = updates[inner_name]
+ updated = updated.Substitute(inner_name, inner_expression)
+ if updated.Equals(outer_expression):
+ break
+ if outer_name in updates and updated.Equals(updates[outer_name]):
+ break
+ updates[outer_name] = updated
+ return updates
diff --git a/tools/perf/pmu-events/metric_test.py b/tools/perf/pmu-events/metric_test.py
index 15315d0f716c..ced5998bd827 100644
--- a/tools/perf/pmu-events/metric_test.py
+++ b/tools/perf/pmu-events/metric_test.py
@@ -2,7 +2,9 @@
import unittest
from metric import Constant
from metric import Event
+from metric import Expression
from metric import ParsePerfJson
+from metric import RewriteMetricsInTermsOfOthers


class TestMetricExpressions(unittest.TestCase):
@@ -153,5 +155,13 @@ class TestMetricExpressions(unittest.TestCase):
after = '0 * SLOTS'
self.assertEqual(ParsePerfJson(before).Simplify().ToPerfJson(), after)

+ def test_RewriteMetricsInTermsOfOthers(self):
+ Expression.__eq__ = lambda e1, e2: e1.Equals(e2)
+ before = [('m1', ParsePerfJson('a + b + c + d')),
+ ('m2', ParsePerfJson('a + b + c'))]
+ after = {'m1': ParsePerfJson('m2 + d')}
+ self.assertEqual(RewriteMetricsInTermsOfOthers(before), after)
+ Expression.__eq__ = None
+
if __name__ == '__main__':
unittest.main()
--
2.39.0.314.g84b9a713c41-goog
\
 
 \ /
  Last update: 2023-03-26 23:17    [W:0.122 / U:0.456 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site