From 5caff5c4758a31be299ef09b292de29fb83a100d Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Wed, 22 May 2024 11:04:54 +0200 Subject: [PATCH 1/4] tezt/long_test: add measure function similar to measure_and_check_regression to only do the measure --- tezt/lib_performance_regression/long_test.ml | 82 +++++++++++++------ tezt/lib_performance_regression/long_test.mli | 49 +++++++++-- tezt/long_tests/logging.ml | 2 +- tezt/long_tests/prt_client.ml | 6 +- tezt/long_tests/tezt_load_time.ml | 2 +- 5 files changed, 104 insertions(+), 37 deletions(-) diff --git a/tezt/lib_performance_regression/long_test.ml b/tezt/lib_performance_regression/long_test.ml index 9f1761d8a27e..3911c61d5524 100644 --- a/tezt/lib_performance_regression/long_test.ml +++ b/tezt/lib_performance_regression/long_test.ml @@ -784,20 +784,24 @@ let check_time_preconditions measurement = if String.contains measurement '\n' then invalid_arg "Long_test.time: newline character in measurement" +let measure_data_points ~repeat ~tags measurement f = + let data_points = ref [] in + for _ = 1 to repeat do + let duration = f () in + let data_point = + InfluxDB.data_point ~tags measurement ("duration", Float duration) + in + add_data_point data_point ; + data_points := data_point :: !data_points + done ; + !data_points + let measure_and_check_regression ?previous_count ?minimum_previous_count ?margin ?check ?stddev ?(repeat = 1) ?(tags = []) measurement f = check_time_preconditions measurement ; if repeat <= 0 then unit else - let data_points = ref [] in - for _ = 1 to repeat do - let duration = f () in - let data_point = - InfluxDB.data_point ~tags measurement ("duration", Float duration) - in - add_data_point data_point ; - data_points := data_point :: !data_points - done ; + let data_points = measure_data_points ~repeat ~tags measurement f in check_regression ?previous_count ?minimum_previous_count @@ -805,12 +809,23 @@ let measure_and_check_regression ?previous_count ?minimum_previous_count ?margin ?check ?stddev ~tags - ~data_points:!data_points + ~data_points measurement "duration" -let time ?previous_count ?minimum_previous_count ?margin ?check ?stddev ?repeat - ?tags measurement f = +let measure ?(repeat = 1) ?(tags = []) measurement f = + let _data_points = measure_data_points ~repeat ~tags measurement f in + () + +let time ?repeat ?tags measurement f = + measure ?repeat ?tags measurement (fun () -> + let start = Unix.gettimeofday () in + f () ; + let stop = Unix.gettimeofday () in + stop -. start) + +let time_and_check_regression ?previous_count ?minimum_previous_count ?margin + ?check ?stddev ?repeat ?tags measurement f = measure_and_check_regression ?previous_count ?minimum_previous_count @@ -826,22 +841,26 @@ let time ?previous_count ?minimum_previous_count ?margin ?check ?stddev ?repeat let stop = Unix.gettimeofday () in stop -. start) +let measure_data_points_lwt ~repeat ~tags measurement f = + let data_points = ref [] in + let* () = + Base.repeat repeat @@ fun () -> + let* duration = f () in + let data_point = + InfluxDB.data_point ~tags measurement ("duration", Float duration) + in + add_data_point data_point ; + data_points := data_point :: !data_points ; + unit + in + return !data_points + let measure_and_check_regression_lwt ?previous_count ?minimum_previous_count ?margin ?check ?stddev ?(repeat = 1) ?(tags = []) measurement f = check_time_preconditions measurement ; if repeat <= 0 then unit else - let data_points = ref [] in - let* () = - Base.repeat repeat @@ fun () -> - let* duration = f () in - let data_point = - InfluxDB.data_point ~tags measurement ("duration", Float duration) - in - add_data_point data_point ; - data_points := data_point :: !data_points ; - unit - in + let* data_points = measure_data_points_lwt ~repeat ~tags measurement f in check_regression ?previous_count ?minimum_previous_count @@ -849,12 +868,23 @@ let measure_and_check_regression_lwt ?previous_count ?minimum_previous_count ?check ?stddev ~tags - ~data_points:!data_points + ~data_points measurement "duration" -let time_lwt ?previous_count ?minimum_previous_count ?margin ?check ?stddev - ?repeat ?tags measurement f = +let measure_lwt ?(repeat = 1) ?(tags = []) measurement f = + let* _data_points = measure_data_points_lwt ~repeat ~tags measurement f in + Lwt.return_unit + +let time_lwt ?repeat ?tags measurement f = + measure_lwt ?repeat ?tags measurement (fun () -> + let start = Unix.gettimeofday () in + let* () = f () in + let stop = Unix.gettimeofday () in + Lwt.return (stop -. start)) + +let time_and_check_regression_lwt ?previous_count ?minimum_previous_count + ?margin ?check ?stddev ?repeat ?tags measurement f = measure_and_check_regression_lwt ?previous_count ?minimum_previous_count diff --git a/tezt/lib_performance_regression/long_test.mli b/tezt/lib_performance_regression/long_test.mli index 25acbc0d5477..1cabcc57f1b1 100644 --- a/tezt/lib_performance_regression/long_test.mli +++ b/tezt/lib_performance_regression/long_test.mli @@ -306,19 +306,27 @@ val check_regression : Usage: [time measurement f] This executes [f], measures the [time] it takes to run, adds - a data point for [measurement] with field ["duration"] equal to [time], - and uses [check_regression] to compare with previous values. + a data point for [measurement] with field ["duration"] equal to [time]. If [f] raises an exception, data points are not pushed and the exception is propagated. If [repeat] is specified, call [f] [repeat] times to obtain as many data points. - See {!check_regression} for documentation about other optional parameters. - @raise Invalid_arg if no test is currently running, or if it was not registered with [Long_test.register] or [Long_test.register_with_protocol]. *) val time : + ?repeat:int -> + ?tags:(string * string) list -> + InfluxDB.measurement -> + (unit -> unit) -> + unit + +(** Same as {time} but uses [check_regression] to compare with previous + values. + + See {!check_regression} for documentation about optional parameters. *) +val time_and_check_regression : ?previous_count:int -> ?minimum_previous_count:int -> ?margin:float -> @@ -330,7 +338,15 @@ val time : (unit -> unit) -> unit Lwt.t -(** Same as {!time}, but instead of measuring the duration taken +(** Same as {!time}, but only record the duration returned by [f]. *) +val measure : + ?repeat:int -> + ?tags:(string * string) list -> + InfluxDB.measurement -> + (unit -> float) -> + unit + +(** Same as {!time_and_check_regression}, but instead of measuring the duration taken by [f ()] execution, delegates this responsability to [f] itself. In this case, [f] represents a thunk that executes an expression @@ -352,6 +368,17 @@ val measure_and_check_regression : Note that other concurrent promises may slow down the measured function and result in inaccurate measurements. *) val time_lwt : + ?repeat:int -> + ?tags:(string * string) list -> + InfluxDB.measurement -> + (unit -> unit Lwt.t) -> + unit Lwt.t + +(** Same as {!time_lwt}, but uses [check_regression] to compare with previous + values. + + See {!check_regression} for documentation about optional parameters. *) +val time_and_check_regression_lwt : ?previous_count:int -> ?minimum_previous_count:int -> ?margin:float -> @@ -363,8 +390,16 @@ val time_lwt : (unit -> unit Lwt.t) -> unit Lwt.t -(** Same as {!time_lwt}, but instead of measuring the duration taken - by [f ()] execution, delegates to [f] itself. +(** Same as {!time_lwt}, but only record the duration returned by [f]. *) +val measure_lwt : + ?repeat:int -> + ?tags:(string * string) list -> + InfluxDB.measurement -> + (unit -> float Lwt.t) -> + unit Lwt.t + +(** Same as {!time_and_check_regression_lwt}, but instead of measuring the + duration taken by [f ()] execution, delegates to [f] itself. In this case, [f] represents a thunk that executes an expression or a program and evaluates in the duration taken by its execution.*) diff --git a/tezt/long_tests/logging.ml b/tezt/long_tests/logging.ml index 4e3862611d23..691295f60871 100644 --- a/tezt/long_tests/logging.ml +++ b/tezt/long_tests/logging.ml @@ -78,7 +78,7 @@ let test_simple_event_logging_time ~executors title simple_event = (* Even with [nb_repeat = 10_000_000], the measurements are not very precise: we observed some alerts where the duration was up to 33% longer than the average. We thus use a threshold of [margin = 0.5] to avoid alert fatigue. *) - Long_test.time_lwt title ~margin:0.5 @@ fun () -> + Long_test.time_and_check_regression_lwt title ~margin:0.5 @@ fun () -> repeat nb_repeat @@ fun () -> Simple.emit simple_event (id, 52) module Test_sink : SINK = struct diff --git a/tezt/long_tests/prt_client.ml b/tezt/long_tests/prt_client.ml index b756ba8a0585..814d85b4e208 100644 --- a/tezt/long_tests/prt_client.ml +++ b/tezt/long_tests/prt_client.ml @@ -61,7 +61,8 @@ let client_load_time ~executors () = ~executors @@ fun () -> let client = Client.create () in - Long_test.time_lwt ~repeat:20 load_time @@ fun () -> Client.version client + Long_test.time_and_check_regression_lwt ~repeat:20 load_time @@ fun () -> + Client.version client let get_blocks_response_time ~executors ~protocol = Long_test.register @@ -73,7 +74,8 @@ let get_blocks_response_time ~executors ~protocol = ~executors @@ fun () -> let* _node, client = Client.init_with_protocol `Client ~protocol () in - Long_test.time_lwt ~repeat:20 response_time_measurement @@ fun () -> + Long_test.time_and_check_regression_lwt ~repeat:20 response_time_measurement + @@ fun () -> let* _ = Client.RPC.call client @@ RPC.get_chain_block () in unit diff --git a/tezt/long_tests/tezt_load_time.ml b/tezt/long_tests/tezt_load_time.ml index abd6b02d2b08..0947eca8d325 100644 --- a/tezt/long_tests/tezt_load_time.ml +++ b/tezt/long_tests/tezt_load_time.ml @@ -55,7 +55,7 @@ let test_tezt_tests_suite_load_time ~executors () = ~executors @@ fun () -> let* () = Process.run "dune" ["build"; "tezt/tests/main.exe"] in - Long_test.time_lwt ~repeat:5 tezt_load_time @@ fun () -> + Long_test.time_and_check_regression_lwt ~repeat:5 tezt_load_time @@ fun () -> let* () = (* [--list] will ensure that we register all tests, and so get a reasonable estimation of start up time. [--only 1] is passed to -- GitLab From d3806becac9d0c4ac0d198cb3c375f84db442ce7 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Wed, 22 May 2024 09:02:43 +0200 Subject: [PATCH 2/4] Tezt/long_tests: disable check regression in pipelining tests Co-authored-by: Arvid Jakobsson --- tezt/long_tests/pipelining.ml | 36 +++++++++++------------------------ 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/tezt/long_tests/pipelining.ml b/tezt/long_tests/pipelining.ml index f48b98c76da3..bdeac5334cd3 100644 --- a/tezt/long_tests/pipelining.ml +++ b/tezt/long_tests/pipelining.ml @@ -370,16 +370,9 @@ let revealing_additional_bootstrap_accounts additional_bootstraps (* Test *) let operation_and_block_validation protocol manager_kind tag = - let margin = - (* match manager_kind with `Call | `Origination -> 1. | `Transfer -> 0.5 *) - 10. - (* Due to unreliable results we set a high margin to avoid unwanted alerts. *) - in let color = Log.Color.FG.green in let tags = [(tag, tag)] in - let measure_and_check_regression ?margin time f = - Long_test.measure_and_check_regression ?margin ~tags time (fun () -> f) - in + let measure time f = Long_test.measure ~tags time (fun () -> f) in let get_previous_stats time_mean time title = let* res = Long_test.get_previous_stats @@ -390,11 +383,12 @@ let operation_and_block_validation protocol manager_kind tag = "duration" Long_test.Stats.mean in - match res with - | None -> unit + (match res with + | None -> () | Some (count, average) -> Log.info ~color "%s:%s, count:%d, average:%f" title tag count average ; - measure_and_check_regression ~margin:1. time_mean average + measure time_mean average) ; + unit in Log.info @@ -501,9 +495,7 @@ let operation_and_block_validation protocol manager_kind tag = "Classification time on %s: %f" (Node.name node2) !classification_time ; - let* () = - measure_and_check_regression ~margin classify_title !classification_time - in + measure classify_title !classification_time ; Log.info "Measure the time that take a node to reclassify %d operations after a \ @@ -541,9 +533,7 @@ let operation_and_block_validation protocol manager_kind tag = "Reclassification time on %s: %f" (Node.name node2) !reclassification_time ; - let* () = - measure_and_check_regression ~margin reclassify_title !reclassification_time - in + measure reclassify_title !reclassification_time ; Log.info "Ensure that the mempool contains %d validated operations" @@ -614,24 +604,20 @@ let operation_and_block_validation protocol manager_kind tag = let* injecting_timestamp = get_timestamp_of_event "injecting_block.v0" io in let forging_time = injecting_timestamp -. prepare_timestamp in Log.info ~color "Block forging time on node A : %f" forging_time ; - let* () = measure_and_check_regression ~margin forging_title forging_time in + measure forging_title forging_time ; let* () = node_t in let* lvl = Node.wait_for_level node2 (lvl + 1) in Log.info ~color "Block validation time on node B: %f" !validation_time ; - let* () = - measure_and_check_regression ~margin validation_title !validation_time - in + measure validation_title !validation_time ; Log.info ~color "Block application time on node B: %f" !application_time ; - let* () = - measure_and_check_regression ~margin application_title !application_time - in + measure application_title !application_time ; let total_time = !application_time +. !validation_time in Log.info ~color "Block validation + application time on node B: %f" total_time ; - let* () = measure_and_check_regression ~margin total_title total_time in + measure total_title total_time ; Log.info "Ensure that the block baked contains %d operations" -- GitLab From 77801659cb8f89e4642518213814ef0c84154393 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Wed, 22 May 2024 13:23:59 +0200 Subject: [PATCH 3/4] tezt/long_tests: add layer1 tag --- tezt/long_tests/block_validation.ml | 6 ++++++ tezt/long_tests/pipelining.ml | 3 +++ tezt/long_tests/prt_client.ml | 4 ++++ tezt/long_tests/qcheck_rpc.ml | 3 +++ tezt/long_tests/script_cache.ml | 5 +++++ tezt/long_tests/team.ml | 2 ++ tezt/long_tests/tenderbake.ml | 4 ++++ 7 files changed, 27 insertions(+) diff --git a/tezt/long_tests/block_validation.ml b/tezt/long_tests/block_validation.ml index c356c33865af..8d44505c33d7 100644 --- a/tezt/long_tests/block_validation.ml +++ b/tezt/long_tests/block_validation.ml @@ -30,6 +30,8 @@ Subject: Benchmarking the validation of blocks *) +let team = Team.layer1 + (** This module contains benchmarks that are used to prevent the regression of performance in the block validation processing. @@ -513,6 +515,7 @@ let register ~executors () = ~__FILE__ ~title:Benchmark.chunk_title ~tags:["shell"; "validation"; "block"; "chunk"; Tag.ci_disabled] + ~team ~timeout:(Long_test.Minutes 20) ~executors @@ apply_or_raise datadir @@ -522,6 +525,7 @@ let register ~executors () = ~__FILE__ ~title:Benchmark.specific_title ~tags:["shell"; "validation"; "block"; "specific"; Tag.ci_disabled] + ~team ~timeout:(Long_test.Minutes 20) ~executors @@ apply_or_raise datadir @@ -534,6 +538,7 @@ let register ~executors () = ~__FILE__ ~title:Benchmark.subparts_title ~tags:["shell"; "validation"; "block"; "subpart"; Tag.ci_disabled] + ~team ~timeout:(Long_test.Minutes 20) ~executors @@ apply_or_raise datadir @@ -553,6 +558,7 @@ let register_semantic_regression_test ~executors () = ~__FILE__ ~title:"shell.validation.replay" ~tags:["shell"; "validation"; "replay"] + ~team ~timeout:(Long_test.Hours 7) ~executors @@ Semantic.replay ~seed ~chunk_size:500 ~runtime:Mtime.Span.(6 * hour) diff --git a/tezt/long_tests/pipelining.ml b/tezt/long_tests/pipelining.ml index bdeac5334cd3..68574202c77f 100644 --- a/tezt/long_tests/pipelining.ml +++ b/tezt/long_tests/pipelining.ml @@ -31,6 +31,8 @@ validator time and the baker forging time. *) +let team = Team.layer1 + (** {2 Test parameters} *) let manager_kinds = [`Transfer; `Origination; `Call] @@ -659,6 +661,7 @@ let operation_and_block_validation ~executors = [ "flush"; "classification"; "prevalidator"; "block"; "baker"; "pipelining"; ] + ~team ~executors ~timeout:(Hours 2) @@ fun () -> diff --git a/tezt/long_tests/prt_client.ml b/tezt/long_tests/prt_client.ml index 814d85b4e208..812546df8fa0 100644 --- a/tezt/long_tests/prt_client.ml +++ b/tezt/long_tests/prt_client.ml @@ -30,6 +30,8 @@ Subject: check regressions in the duration it takes for the client to load. *) +let team = Team.layer1 + let load_time = "client load time" let response_time_test = "get blocks time" @@ -56,6 +58,7 @@ let client_load_time ~executors () = ~__FILE__ ~title:load_time ~tags:["client"; "load"] + ~team ~timeout:(Minutes 2) ~uses_node:true ~executors @@ -69,6 +72,7 @@ let get_blocks_response_time ~executors ~protocol = ~__FILE__ ~title:response_time_test ~tags:["rpc"] + ~team ~timeout:(Minutes 3) ~uses_node:true ~executors diff --git a/tezt/long_tests/qcheck_rpc.ml b/tezt/long_tests/qcheck_rpc.ml index 3ee13e93fc92..bfe75e8b2a22 100644 --- a/tezt/long_tests/qcheck_rpc.ml +++ b/tezt/long_tests/qcheck_rpc.ml @@ -30,6 +30,8 @@ Subject: Property testing the RPC server *) +let team = Team.layer1 + (* {0 Description} @@ -548,6 +550,7 @@ let property_test_rpc_server ~executors = ~__FILE__ ~title:"property_test_rpc_server" ~tags:["node"; "pbt"; "fuzz"; "rpc"] + ~team ~executors ~timeout:(Hours 1) Test.test_rpc_server diff --git a/tezt/long_tests/script_cache.ml b/tezt/long_tests/script_cache.ml index c2cb85a90eda..d80dfe6f72b7 100644 --- a/tezt/long_tests/script_cache.ml +++ b/tezt/long_tests/script_cache.ml @@ -38,6 +38,8 @@ These test have disabled by adding the tag [ci_disabled] due to consistent failures. *) +let team = Team.layer1 + (* Helpers @@ -276,6 +278,7 @@ let check ?(tags = []) label test ~protocol ~executors = ~__FILE__ ~title:(sf "(%s) Cache: %s" (Protocol.name protocol) label) ~tags:(["cache"] @ tags) + ~team ~timeout:(Minutes 2000) ~executors @@ test @@ -688,6 +691,7 @@ let check_simulation_close_to_protocol_user_activation ~executors ~migrate_from (Protocol.name migrate_from) (Protocol.name migrate_to)) ~tags:["cache"; "simulation"; "user"; "upgrade"; Tag.ci_disabled] + ~team ~timeout:(Minutes 2000) ~executors @@ fun () -> @@ -787,6 +791,7 @@ let check_simulation_close_to_protocol_auto_activation ~executors ~migrate_from (Protocol.name migrate_from) (Protocol.name migrate_to)) ~tags:["cache"; "simulation"; "auto"; "upgrade"; Tag.ci_disabled] + ~team ~timeout:(Minutes 2000) ~executors @@ fun () -> diff --git a/tezt/long_tests/team.ml b/tezt/long_tests/team.ml index 720c9fcd020c..86a001509f53 100644 --- a/tezt/long_tests/team.ml +++ b/tezt/long_tests/team.ml @@ -6,3 +6,5 @@ (*****************************************************************************) let infrastructure = "infrastructure" + +let layer1 = "layer1" diff --git a/tezt/long_tests/tenderbake.ml b/tezt/long_tests/tenderbake.ml index 57602e6c01f2..a3fce5f42514 100644 --- a/tezt/long_tests/tenderbake.ml +++ b/tezt/long_tests/tenderbake.ml @@ -30,6 +30,8 @@ Subject: Checking performance for Tenderbake bakers *) +let team = Team.layer1 + module Time = Tezos_base.Time.System let lwt_ignore p = @@ -311,6 +313,7 @@ module Rounds = struct ~__FILE__ ~title:test ~tags:["tenderbake"; "basic"] + ~team ~uses:[Protocol.baker protocol] ~executors ~timeout:(Long_test.Seconds (repeat * 8 * timeout)) @@ -462,6 +465,7 @@ module Long_dynamic_bake = struct ~__FILE__ ~title:(test topology) ~tags:["tenderbake"; "dynamic"; string_of_topology topology] + ~team ~uses:[Protocol.baker protocol] ~executors ~timeout:(Long_test.Seconds (repeat * 8 * timeout)) -- GitLab From 10b38e4762bc1b19657f3fd49bbef9298383d7c1 Mon Sep 17 00:00:00 2001 From: Romain Date: Wed, 22 May 2024 17:35:31 +0200 Subject: [PATCH 4/4] Long_test: improve documentation --- tezt/lib_performance_regression/long_test.mli | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/tezt/lib_performance_regression/long_test.mli b/tezt/lib_performance_regression/long_test.mli index 1cabcc57f1b1..cce47226bd90 100644 --- a/tezt/lib_performance_regression/long_test.mli +++ b/tezt/lib_performance_regression/long_test.mli @@ -305,7 +305,7 @@ val check_regression : Usage: [time measurement f] - This executes [f], measures the [time] it takes to run, adds + This executes [f], measures the [time] it takes to run, and adds a data point for [measurement] with field ["duration"] equal to [time]. If [f] raises an exception, data points are not pushed and the exception is propagated. @@ -322,10 +322,7 @@ val time : (unit -> unit) -> unit -(** Same as {time} but uses [check_regression] to compare with previous - values. - - See {!check_regression} for documentation about optional parameters. *) +(** Same as {!time} followed by {!check_regression}. *) val time_and_check_regression : ?previous_count:int -> ?minimum_previous_count:int -> @@ -338,7 +335,12 @@ val time_and_check_regression : (unit -> unit) -> unit Lwt.t -(** Same as {!time}, but only record the duration returned by [f]. *) +(** Same as {!time}, but let the function choose how it measures time. + + {!time} measures time using [Unix.gettimeofday] around the whole execution + of the function. If you only want to measure parts of the execution, + or if you want to use something more precise than [Unix.gettimeofday], + or if you want to measure something which is not time, use [measure] instead. *) val measure : ?repeat:int -> ?tags:(string * string) list -> @@ -346,11 +348,7 @@ val measure : (unit -> float) -> unit -(** Same as {!time_and_check_regression}, but instead of measuring the duration taken - by [f ()] execution, delegates this responsability to [f] itself. - - In this case, [f] represents a thunk that executes an expression - or a program and evaluates in the duration taken by its execution.*) +(** Same as {!measure} followed by {!check_regression}. *) val measure_and_check_regression : ?previous_count:int -> ?minimum_previous_count:int -> @@ -374,10 +372,7 @@ val time_lwt : (unit -> unit Lwt.t) -> unit Lwt.t -(** Same as {!time_lwt}, but uses [check_regression] to compare with previous - values. - - See {!check_regression} for documentation about optional parameters. *) +(** Same as {!time_and_check_regression}, but for functions that return promises. *) val time_and_check_regression_lwt : ?previous_count:int -> ?minimum_previous_count:int -> @@ -390,7 +385,7 @@ val time_and_check_regression_lwt : (unit -> unit Lwt.t) -> unit Lwt.t -(** Same as {!time_lwt}, but only record the duration returned by [f]. *) +(** Same as {!measure}, but for functions that return promises. *) val measure_lwt : ?repeat:int -> ?tags:(string * string) list -> @@ -398,11 +393,7 @@ val measure_lwt : (unit -> float Lwt.t) -> unit Lwt.t -(** Same as {!time_and_check_regression_lwt}, but instead of measuring the - duration taken by [f ()] execution, delegates to [f] itself. - - In this case, [f] represents a thunk that executes an expression - or a program and evaluates in the duration taken by its execution.*) +(** Same as {!measure_and_check_regression}, but for functions that return promises. *) val measure_and_check_regression_lwt : ?previous_count:int -> ?minimum_previous_count:int -> -- GitLab