@@ -2693,7 +2693,8 @@ def rules(self, out, benchmarks, bmSuiteArgs):
2693
2693
"ktor-hello-world" : {},
2694
2694
"play-scala-hello-world" : {},
2695
2695
},
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 ],
2697
2698
"rss_percentiles" : [100 , 99 , 98 , 97 , 96 , 95 , 90 , 75 , 50 , 25 ],
2698
2699
"disable_trackers" : [mx_benchmark .RssTracker , mx_benchmark .PsrecordTracker , mx_benchmark .PsrecordMaxrssTracker , mx_benchmark .RssPercentilesTracker , mx_benchmark .RssPercentilesAndMaxTracker ],
2699
2700
}
@@ -2825,43 +2826,6 @@ def rules(self, out, benchmarks, bmSuiteArgs):
2825
2826
"load-tester.id" : ("<startup.id>" , str ),
2826
2827
"load-tester.method-type" : "requests"
2827
2828
}, ["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" ]))
2865
2829
2866
2830
# Warmup
2867
2831
all_rules .append (mx_benchmark .JsonArrayStdOutFileRule (json_file_pattern , json_file_group_name , {
@@ -2920,21 +2884,66 @@ def parse(self, text) -> Iterable[DataPoint]:
2920
2884
}, [
2921
2885
f"resource_usage__rss__p{ float (percentile )} "
2922
2886
], 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 = "__" ))
2933
2887
2934
2888
return all_rules
2935
2889
2936
2890
def validateStdoutWithDimensions (self , out , benchmarks , bmSuiteArgs , retcode = None , dims = None , extraRules = None ) -> DataPoints :
2937
2891
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
+ """
2938
2947
for datapoint in datapoints :
2939
2948
# Expand the 'load-tester' field group
2940
2949
if "load-tester.command" in datapoint :
0 commit comments