-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
benchmark_diff: A Python script for diffing summarized benchmarks fro…
…m external tests
- Loading branch information
Showing
4 changed files
with
720 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
#!/usr/bin/env python3 | ||
|
||
from argparse import ArgumentParser | ||
from dataclasses import dataclass | ||
from enum import Enum | ||
from pathlib import Path | ||
from typing import Any, Optional, Union | ||
import json | ||
import sys | ||
|
||
|
||
class DifferenceStyle(Enum): | ||
ABSOLUTE = 'absolute' | ||
RELATIVE = 'relative' | ||
HUMANIZED = 'humanized' | ||
|
||
|
||
DEFAULT_RELATIVE_PRECISION = 4 | ||
DEFAULT_DIFFERENCE_STYLE = DifferenceStyle.ABSOLUTE | ||
|
||
|
||
class ValidationError(Exception): | ||
pass | ||
|
||
|
||
class CommandLineError(ValidationError): | ||
pass | ||
|
||
|
||
class BenchmarkDiffer: | ||
difference_style: DifferenceStyle | ||
relative_precision: Optional[int] | ||
|
||
def __init__( | ||
self, | ||
difference_style: DifferenceStyle, | ||
relative_precision: Optional[int], | ||
): | ||
self.difference_style = difference_style | ||
self.relative_precision = relative_precision | ||
|
||
def run(self, before: Any, after: Any) -> Optional[Union[dict, str, int, float]]: | ||
if not isinstance(before, dict) or not isinstance(after, dict): | ||
return self._diff_scalars(before, after) | ||
|
||
if before.get('version') != after.get('version'): | ||
return self._humanize_diff('!V') | ||
|
||
diff = {} | ||
for key in (set(before) | set(after)) - {'version'}: | ||
value_diff = self.run(before.get(key), after.get(key)) | ||
if value_diff not in [None, {}]: | ||
diff[key] = value_diff | ||
|
||
return diff | ||
|
||
def _diff_scalars(self, before: Any, after: Any) -> Optional[Union[str, int, float, dict]]: | ||
assert not isinstance(before, dict) or not isinstance(after, dict) | ||
|
||
if before is None and after is None: | ||
return {} | ||
if before is None: | ||
return self._humanize_diff('!B') | ||
if after is None: | ||
return self._humanize_diff('!A') | ||
if not isinstance(before, (int, float)) or not isinstance(after, (int, float)): | ||
return self._humanize_diff('!T') | ||
|
||
number_diff = self._diff_numbers(before, after) | ||
if self.difference_style != DifferenceStyle.HUMANIZED: | ||
return number_diff | ||
|
||
return self._humanize_diff(number_diff) | ||
|
||
def _diff_numbers(self, value_before: Union[int, float], value_after: Union[int, float]) -> Union[str, int, float]: | ||
diff: Union[str, int, float] | ||
|
||
if self.difference_style == DifferenceStyle.ABSOLUTE: | ||
diff = value_after - value_before | ||
if isinstance(diff, float) and diff.is_integer(): | ||
diff = int(diff) | ||
|
||
return diff | ||
|
||
if value_before == 0: | ||
if value_after > 0: | ||
return '+INF' | ||
elif value_after < 0: | ||
return '-INF' | ||
else: | ||
return 0 | ||
|
||
diff = (value_after - value_before) / abs(value_before) | ||
if self.relative_precision is not None: | ||
rounded_diff = round(diff, self.relative_precision) | ||
if rounded_diff == 0 and diff < 0: | ||
diff = '-0' | ||
elif rounded_diff == 0 and diff > 0: | ||
diff = '+0' | ||
else: | ||
diff = rounded_diff | ||
|
||
if isinstance(diff, float) and diff.is_integer(): | ||
diff = int(diff) | ||
|
||
return diff | ||
|
||
def _humanize_diff(self, diff: Union[str, int, float]) -> str: | ||
if isinstance(diff, str) and diff.startswith('!'): | ||
return diff | ||
|
||
value: Union[str, int, float] | ||
if isinstance(diff, (int, float)): | ||
value = diff * 100 | ||
if isinstance(value, float) and self.relative_precision is not None: | ||
# The multiplication can result in new significant digits appearing. We need to reround. | ||
# NOTE: round() works fine with negative precision. | ||
value = round(value, self.relative_precision - 2) | ||
if isinstance(value, float) and value.is_integer(): | ||
value = int(value) | ||
prefix = '' | ||
if diff < 0: | ||
prefix = '' | ||
elif diff > 0: | ||
prefix = '+' | ||
else: | ||
value = diff | ||
prefix = '' | ||
|
||
return f"{prefix}{value}%" | ||
|
||
|
||
@dataclass(frozen=True) | ||
class CommandLineOptions: | ||
report_before: Path | ||
report_after: Path | ||
difference_style: DifferenceStyle | ||
relative_precision: int | ||
|
||
|
||
def process_commandline() -> CommandLineOptions: | ||
script_description = ( | ||
"Compares summarized benchmark reports and outputs JSON with the same structure but listing only differences." | ||
) | ||
|
||
parser = ArgumentParser(description=script_description) | ||
parser.add_argument(dest='report_before', help="Path to a JSON file containing original benchmark results.") | ||
parser.add_argument(dest='report_after', help="Path to a JSON file containing new benchmark results.") | ||
parser.add_argument( | ||
'--style', | ||
dest='difference_style', | ||
choices=[s.value for s in DifferenceStyle], | ||
help=( | ||
"How to present numeric differences: " | ||
f"'{DifferenceStyle.ABSOLUTE.value}' subtracts new from original; " | ||
f"'{DifferenceStyle.RELATIVE.value}' also divides by the original; " | ||
f"'{DifferenceStyle.HUMANIZED.value}' is like relative but value is a percentage and " | ||
"positive/negative changes are emphasized. " | ||
f"(default: '{DEFAULT_DIFFERENCE_STYLE}')." | ||
) | ||
) | ||
# NOTE: Negative values are valid for precision. round() handles them in a sensible way. | ||
parser.add_argument( | ||
'--precision', | ||
dest='relative_precision', | ||
type=int, | ||
default=DEFAULT_RELATIVE_PRECISION, | ||
help=( | ||
"Number of significant digits for relative differences. " | ||
f"Note that with --style={DifferenceStyle.HUMANIZED.value} the rounding is applied " | ||
"**before** converting the value to a percentage so you need to add 2. " | ||
f"Has no effect when used together with --style={DifferenceStyle.ABSOLUTE.value}. " | ||
f"(default: {DEFAULT_RELATIVE_PRECISION})" | ||
) | ||
) | ||
|
||
options = parser.parse_args() | ||
|
||
if options.difference_style is not None: | ||
difference_style = DifferenceStyle(options.difference_style) | ||
else: | ||
difference_style = DEFAULT_DIFFERENCE_STYLE | ||
|
||
processed_options = CommandLineOptions( | ||
report_before=Path(options.report_before), | ||
report_after=Path(options.report_after), | ||
difference_style=difference_style, | ||
relative_precision=options.relative_precision, | ||
) | ||
|
||
return processed_options | ||
|
||
|
||
def main(): | ||
try: | ||
options = process_commandline() | ||
|
||
differ = BenchmarkDiffer(options.difference_style, options.relative_precision) | ||
diff = differ.run( | ||
json.loads(options.report_before.read_text('utf-8')), | ||
json.loads(options.report_after.read_text('utf-8')), | ||
) | ||
|
||
print(json.dumps(diff, indent=4, sort_keys=True)) | ||
|
||
return 0 | ||
except CommandLineError as exception: | ||
print(f"ERROR: {exception}", file=sys.stderr) | ||
return 1 | ||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
100 changes: 100 additions & 0 deletions
100
test/scripts/fixtures/summarized-benchmarks-branch.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
{ | ||
"bleeps": { | ||
"ir-optimize-evm+yul": { | ||
"bytecode_size": 132868, | ||
"deployment_gas": 0, | ||
"method_gas": 39289198, | ||
"version": "bb90cd0" | ||
}, | ||
"legacy-optimize-evm+yul": { | ||
"bytecode_size": 137869, | ||
"deployment_gas": 0, | ||
"method_gas": 38863224, | ||
"version": "bb90cd0" | ||
} | ||
}, | ||
"colony": { | ||
"legacy-no-optimize": { | ||
"bytecode_size": 664190, | ||
"deployment_gas": null, | ||
"method_gas": null, | ||
"version": "573399b" | ||
}, | ||
"legacy-optimize-evm+yul": { | ||
"bytecode_size": 363606, | ||
"deployment_gas": null, | ||
"method_gas": null, | ||
"version": "573399b" | ||
} | ||
}, | ||
"elementfi": { | ||
"legacy-no-optimize": { | ||
"bytecode_size": null, | ||
"deployment_gas": 69200158, | ||
"method_gas": null, | ||
"version": "87f8b5e" | ||
}, | ||
"legacy-optimize-evm+yul": { | ||
"deployment_gas": 40951128, | ||
"version": "87f8b5e" | ||
}, | ||
"ir-optimize-evm-only": {}, | ||
"ir-no-optimize": { | ||
"deployment_gas": null, | ||
"method_gas": 2777867251, | ||
"version": "87f8b5e" | ||
} | ||
}, | ||
"euler": { | ||
"ir-no-optimize": { | ||
"bytecode_size": 328540, | ||
"deployment_gas": 61591870, | ||
"method_gas": 3537419168, | ||
"version": "2ef99fc" | ||
}, | ||
"legacy-no-optimize": { | ||
"bytecode_size": 328540, | ||
"deployment_gas": 62590688, | ||
"method_gas": 3537419168, | ||
"version": "2ef99fc" | ||
}, | ||
"legacy-optimize-evm+yul": { | ||
"bytecode_size": 182190, | ||
"deployment_gas": 35236828, | ||
"method_gas": 2777867251, | ||
"version": "2ef99fc" | ||
}, | ||
"legacy-optimize-evm-only": { | ||
"bytecode_size": 205211, | ||
"deployment_gas": 39459629, | ||
"method_gas": 2978467272, | ||
"version": "2ef99fc" | ||
}, | ||
"ir-optimize-evm-only": { | ||
"bytecode_size": 205211, | ||
"deployment_gas": 39459629, | ||
"method_gas": 2978467272, | ||
"version": "2ef99fc" | ||
}, | ||
"ir-optimize-evm+yul": { | ||
"bytecode_size": 205211, | ||
"deployment_gas": 39459629, | ||
"method_gas": 2777867251 | ||
} | ||
}, | ||
"gnosis": { | ||
"ir-optimize-evm+yul": { | ||
"bytecode_size": 56069, | ||
"deployment_gas": null, | ||
"method_gas": null, | ||
"version": "ea09294" | ||
} | ||
}, | ||
"zeppelin": { | ||
"legacy-optimize-evm+yul": { | ||
"bytecode_size": 510428, | ||
"deployment_gas": 94501114, | ||
"version": "af7ec04" | ||
} | ||
} | ||
} |
Oops, something went wrong.