Skip to content

Commit ec16e70

Browse files
author
Andrija Kolic
committed
[GR-64438] [GR-64720] Add ops-per-GB-second metric to Barista
PullRequest: graal/20711
2 parents 5a30807 + 1333a7e commit ec16e70

File tree

1 file changed

+57
-48
lines changed

1 file changed

+57
-48
lines changed

sdk/mx.sdk/mx_sdk_benchmark.py

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,7 +2693,8 @@ def rules(self, out, benchmarks, bmSuiteArgs):
26932693
"ktor-hello-world": {},
26942694
"play-scala-hello-world": {},
26952695
},
2696-
"latency_percentiles": [50.0, 75.0, 90.0, 99.0, 99.9, 99.99, 99.999, 100.0],
2696+
# Should currently only contain round numbers due to the field incorrectly being indexed as integer in the DB (GR-57487)
2697+
"latency_percentiles": [50.0, 75.0, 90.0, 99.0, 100.0],
26972698
"rss_percentiles": [100, 99, 98, 97, 96, 95, 90, 75, 50, 25],
26982699
"disable_trackers": [mx_benchmark.RssTracker, mx_benchmark.PsrecordTracker, mx_benchmark.PsrecordMaxrssTracker, mx_benchmark.RssPercentilesTracker, mx_benchmark.RssPercentilesAndMaxTracker],
26992700
}
@@ -2825,43 +2826,6 @@ def rules(self, out, benchmarks, bmSuiteArgs):
28252826
"load-tester.id": ("<startup.id>", str),
28262827
"load-tester.method-type": "requests"
28272828
}, ["startup.id", "startup.measurements.iteration", "startup.measurements.response_time"]))
2828-
# copy the response_time with iteration 0 into time-to-first-response
2829-
class DeriveTimeToFirstResponseRule(mx_benchmark.JsonArrayStdOutFileRule):
2830-
def parse(self, text) -> Iterable[DataPoint]:
2831-
datapoints = super().parse(text)
2832-
iteration_0_datapoints = [datapoint for datapoint in datapoints if datapoint["metric.iteration"] == 0]
2833-
for datapoint in iteration_0_datapoints:
2834-
del datapoint["metric.iteration"]
2835-
return iteration_0_datapoints
2836-
all_rules.append(DeriveTimeToFirstResponseRule(json_file_pattern, json_file_group_name, {
2837-
"benchmark": self.context.benchmark,
2838-
"metric.name": "time-to-first-response",
2839-
"metric.type": "numeric",
2840-
"metric.unit": "ms",
2841-
"metric.value": ("<startup.measurements.response_time>", float),
2842-
"metric.better": "lower",
2843-
"metric.iteration": ("<startup.measurements.iteration>", int),
2844-
"load-tester.id": ("<startup.id>", str),
2845-
"load-tester.method-type": "requests"
2846-
}, ["startup.id", "startup.measurements.iteration", "startup.measurements.response_time"]))
2847-
# copy the worst response_time into max-time
2848-
class DeriveMaxTimeRule(mx_benchmark.JsonArrayStdOutFileRule):
2849-
def parse(self, text) -> Iterable[DataPoint]:
2850-
datapoints = super().parse(text)
2851-
if len(datapoints) == 0:
2852-
return []
2853-
max_value_datapoints = [max(datapoints, key=lambda x: x["metric.value"])]
2854-
return max_value_datapoints
2855-
all_rules.append(DeriveMaxTimeRule(json_file_pattern, json_file_group_name, {
2856-
"benchmark": self.context.benchmark,
2857-
"metric.name": "max-time",
2858-
"metric.type": "numeric",
2859-
"metric.unit": "ms",
2860-
"metric.value": ("<startup.measurements.response_time>", float),
2861-
"metric.better": "lower",
2862-
"load-tester.id": ("<startup.id>", str),
2863-
"load-tester.method-type": "requests"
2864-
}, ["startup.id", "startup.measurements.response_time"]))
28652829

28662830
# Warmup
28672831
all_rules.append(mx_benchmark.JsonArrayStdOutFileRule(json_file_pattern, json_file_group_name, {
@@ -2920,21 +2884,66 @@ def parse(self, text) -> Iterable[DataPoint]:
29202884
}, [
29212885
f"resource_usage__rss__p{float(percentile)}"
29222886
], indexer_str="__") for percentile in _baristaConfig["rss_percentiles"]]
2923-
# Ensure we are reporting the analogous numbers across suites (p99 at the time of writing this comment)
2924-
percentile_to_copy_into_max_rss = float(mx_benchmark.RssPercentilesTracker.MaxRssCopyRule.percentile_to_copy_into_max_rss)
2925-
all_rules.append(mx_benchmark.JsonArrayStdOutFileRule(json_file_pattern, json_file_group_name, {
2926-
"benchmark": self.context.benchmark,
2927-
"metric.name": "max-rss",
2928-
"metric.type": "numeric",
2929-
"metric.unit": "MB",
2930-
"metric.value": (f"<resource_usage__rss__p{percentile_to_copy_into_max_rss}>", float),
2931-
"metric.better": "lower",
2932-
}, [f"resource_usage__rss__p{percentile_to_copy_into_max_rss}"], indexer_str="__"))
29332887

29342888
return all_rules
29352889

29362890
def validateStdoutWithDimensions(self, out, benchmarks, bmSuiteArgs, retcode=None, dims=None, extraRules=None) -> DataPoints:
29372891
datapoints = super().validateStdoutWithDimensions(out, benchmarks, bmSuiteArgs, retcode=retcode, dims=dims, extraRules=extraRules)
2892+
datapoints = self.computeDerivedDatapoints(datapoints)
2893+
datapoints = self.extendDatapoints(datapoints)
2894+
return datapoints
2895+
2896+
def computeDerivedDatapoints(self, datapoints: DataPoints) -> DataPoints:
2897+
"""Adds derived datapoints to the list of datapoints captured from the benchmark stdout or generated files.
2898+
Adds datapoints such as:
2899+
* max-rss: copied from specific rss percentile values
2900+
* time-to-first-response: copied from response_time with iteration 0
2901+
* max-time: copied from response_time with the highest value
2902+
* ops-per-GB-second: computed as throughput divided by max-rss
2903+
"""
2904+
# max-rss
2905+
percentile_to_copy_into_max_rss = float(mx_benchmark.RssPercentilesTracker.MaxRssCopyRule.percentile_to_copy_into_max_rss)
2906+
rss_dp_to_copy_from = next(filter(lambda dp: dp["metric.name"] == "rss" and dp["metric.percentile"] == percentile_to_copy_into_max_rss, datapoints), None)
2907+
if rss_dp_to_copy_from is not None:
2908+
max_rss_dp = rss_dp_to_copy_from.copy()
2909+
max_rss_dp["metric.name"] = "max-rss"
2910+
del max_rss_dp["metric.percentile"]
2911+
datapoints.append(max_rss_dp)
2912+
2913+
# time-to-first-response
2914+
first_request_time_dp = next(filter(lambda dp: dp["metric.name"] == "request-time" and dp["metric.iteration"] == 0, datapoints), None)
2915+
if first_request_time_dp is not None:
2916+
time_to_first_response_dp = first_request_time_dp.copy()
2917+
time_to_first_response_dp["metric.name"] = "time-to-first-response"
2918+
del time_to_first_response_dp["metric.iteration"]
2919+
datapoints.append(time_to_first_response_dp)
2920+
2921+
# max-time
2922+
request_time_dps = filter(lambda dp: dp["metric.name"] == "request-time", datapoints)
2923+
worst_request_time_dp = max(request_time_dps, key=lambda dp: dp["metric.value"], default=None)
2924+
if worst_request_time_dp is not None:
2925+
max_time_dp = worst_request_time_dp.copy()
2926+
max_time_dp["metric.name"] = "max-time"
2927+
del max_time_dp["metric.iteration"]
2928+
datapoints.append(max_time_dp)
2929+
2930+
# ops-per-GB-second
2931+
throughput_dp = next(filter(lambda dp: dp["metric.name"] == "throughput", datapoints), None)
2932+
if rss_dp_to_copy_from is not None and throughput_dp is not None:
2933+
ops_per_gb_sec = throughput_dp["metric.value"] / (max_rss_dp["metric.value"] / 1024)
2934+
ops_per_gb_sec_dp = throughput_dp.copy()
2935+
ops_per_gb_sec_dp["metric.name"] = "ops-per-GB-second"
2936+
ops_per_gb_sec_dp["metric.unit"] = "op/GB*s"
2937+
ops_per_gb_sec_dp["metric.value"] = ops_per_gb_sec
2938+
datapoints.append(ops_per_gb_sec_dp)
2939+
2940+
return datapoints
2941+
2942+
def extendDatapoints(self, datapoints: DataPoints) -> DataPoints:
2943+
"""
2944+
Extends the datapoints with 'load-tester' fields.
2945+
Relies on the intermediate 'load-tester.command' field being set up beforehand.
2946+
"""
29382947
for datapoint in datapoints:
29392948
# Expand the 'load-tester' field group
29402949
if "load-tester.command" in datapoint:

0 commit comments

Comments
 (0)