benchmark_result.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. # Copyright Materialize, Inc. and contributors. All rights reserved.
  2. #
  3. # Use of this software is governed by the Business Source License
  4. # included in the LICENSE file at the root of this repository.
  5. #
  6. # As of the Change Date specified in that file, in accordance with
  7. # the Business Source License, use of this software will be governed
  8. # by the Apache License, Version 2.0.
  9. from __future__ import annotations
  10. from typing import Generic, TypeVar
  11. from materialize.feature_benchmark.measurement import MeasurementType, MeasurementUnit
  12. from materialize.feature_benchmark.scenario import Scenario
  13. from materialize.feature_benchmark.scenario_version import ScenarioVersion
  14. T = TypeVar("T")
  15. class BenchmarkScenarioResult:
  16. def __init__(
  17. self, scenario_class: type[Scenario], measurement_types: list[MeasurementType]
  18. ):
  19. self.scenario_class = scenario_class
  20. self.scenario_name = scenario_class.__name__
  21. self.version: ScenarioVersion | None = None
  22. self.metrics: list[BenchmarkScenarioMetric] = []
  23. for measurement_type in measurement_types:
  24. self.metrics.append(
  25. BenchmarkScenarioMetric(scenario_class, measurement_type)
  26. )
  27. def set_scenario_version(self, version: ScenarioVersion):
  28. self.version = version
  29. def get_scenario_version(self) -> ScenarioVersion:
  30. assert self.version is not None
  31. return self.version
  32. def empty(self) -> None:
  33. self.metrics = []
  34. def is_empty(self) -> bool:
  35. return len(self.metrics) == 0
  36. def get_metric_by_measurement_type(
  37. self, measurement_type: MeasurementType
  38. ) -> BenchmarkScenarioMetric | None:
  39. for metric in self.metrics:
  40. if metric.measurement_type == measurement_type:
  41. return metric
  42. return None
  43. class BenchmarkScenarioMetric(Generic[T]):
  44. def __init__(
  45. self, scenario_class: type[Scenario], measurement_type: MeasurementType
  46. ) -> None:
  47. self.scenario_class = scenario_class
  48. self.measurement_type = measurement_type
  49. self._points: list[T] = []
  50. self._unit: MeasurementUnit = MeasurementUnit.UNKNOWN
  51. def append_point(
  52. self, point: T, unit: MeasurementUnit, aggregation_name: str
  53. ) -> None:
  54. if self._unit == MeasurementUnit.UNKNOWN:
  55. self._unit = unit
  56. elif point is None:
  57. # ignore unit check
  58. pass
  59. else:
  60. assert (
  61. self._unit == unit
  62. ), f"Mix of units in {self.scenario_class.__name__}: {self._unit} and {unit} in {aggregation_name}"
  63. self._points.append(point)
  64. def this(self) -> T:
  65. return self._points[0]
  66. def points_this(self) -> list[T]:
  67. return self._points
  68. def this_as_str(self) -> str:
  69. if self.this() is None:
  70. return " None"
  71. else:
  72. return f"{self.this():>11.3f}"
  73. def other(self) -> T:
  74. return self._points[1]
  75. def other_as_str(self) -> str:
  76. if self.other() is None:
  77. return " None"
  78. else:
  79. return f"{self.other():>11.3f}"
  80. def unit(self) -> MeasurementUnit:
  81. assert self._unit is not None
  82. return self._unit
  83. def has_values(self) -> bool:
  84. return self.this() is not None or self.other() is not None