From 860ccfcf1fbb419dce78f385fbd87d66e4c0b3c4 Mon Sep 17 00:00:00 2001 From: Jun Furuse Date: Sun, 27 Aug 2023 15:56:36 +0900 Subject: [PATCH 1/7] Devtools: adds benchmarks-tools/occupy_memory --- .../benchmarks-tools/occupy_memory/README.md | 6 ++++ devtools/benchmarks-tools/occupy_memory/dune | 7 ++++ .../occupy_memory/occupy_memory.ml | 36 +++++++++++++++++++ manifest/product_tooling.ml | 11 ++++++ 4 files changed, 60 insertions(+) create mode 100644 devtools/benchmarks-tools/occupy_memory/README.md create mode 100644 devtools/benchmarks-tools/occupy_memory/dune create mode 100644 devtools/benchmarks-tools/occupy_memory/occupy_memory.ml diff --git a/devtools/benchmarks-tools/occupy_memory/README.md b/devtools/benchmarks-tools/occupy_memory/README.md new file mode 100644 index 000000000000..a3a306af5fd1 --- /dev/null +++ b/devtools/benchmarks-tools/occupy_memory/README.md @@ -0,0 +1,6 @@ +[occupy_memory.exe nbytes] allocates [nbytes] bytes, and +wait a line from the standard input. + +It is used to restrict the available memory in IO benchmarks. +See [src/lib_shell_benchmarks/io_benchmarks.ml]. + diff --git a/devtools/benchmarks-tools/occupy_memory/dune b/devtools/benchmarks-tools/occupy_memory/dune new file mode 100644 index 000000000000..cdf50cf490ba --- /dev/null +++ b/devtools/benchmarks-tools/occupy_memory/dune @@ -0,0 +1,7 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(executable + (name occupy_memory) + (public_name occupy_memory) + (package tezos-tooling)) diff --git a/devtools/benchmarks-tools/occupy_memory/occupy_memory.ml b/devtools/benchmarks-tools/occupy_memory/occupy_memory.ml new file mode 100644 index 000000000000..52a423d362a1 --- /dev/null +++ b/devtools/benchmarks-tools/occupy_memory/occupy_memory.ml @@ -0,0 +1,36 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 DaiLambda, Inc. *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +let amount = int_of_string @@ Sys.argv.(1) + +let () = + let bytes = Bytes.init amount (fun _ -> '\000') in + Format.eprintf "Allocated %d bytes@." amount ; + print_endline "allocated" ; + flush stdout ; + let _ = input_line stdin in + Format.eprintf "Deallocated %d bytes@." (Bytes.length bytes) ; + Gc.compact () ; + print_endline "deallocating" diff --git a/manifest/product_tooling.ml b/manifest/product_tooling.ml index c40606d2fb89..cde976488e36 100644 --- a/manifest/product_tooling.ml +++ b/manifest/product_tooling.ml @@ -62,3 +62,14 @@ let _benchmark_tools_purge_disk_cache = ~deps:[] ~static:false ~bisect_ppx:No + +let _benchmark_tools_occupy_memory = + public_exe + "occupy_memory" + ~path:"devtools/benchmarks-tools/occupy_memory" + ~internal_name:"occupy_memory" + ~opam:"tezos-tooling" + ~release_status:Unreleased + ~deps:[] + ~static:false + ~bisect_ppx:No -- GitLab From c7c418b65ad75f3ae5ef8e3c9b761c02a6bde213 Mon Sep 17 00:00:00 2001 From: Jun Furuse Date: Mon, 4 Sep 2023 19:50:13 +0900 Subject: [PATCH 2/7] Shell_benchmarks: adds Io_stats.fold_tree --- src/lib_shell_benchmarks/io_stats.ml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/lib_shell_benchmarks/io_stats.ml b/src/lib_shell_benchmarks/io_stats.ml index 91463e16b542..c9cebcf64488 100644 --- a/src/lib_shell_benchmarks/io_stats.ml +++ b/src/lib_shell_benchmarks/io_stats.ml @@ -100,6 +100,19 @@ let load_tree context key = Io_helpers.Key_map.insert (key @ path) len tree | None -> tree) +let fold_tree base_dir context_hash key init f = + Format.eprintf "Loading the trees of %a@." Context_hash.pp context_hash ; + let open Lwt.Syntax in + let* context, index = + Io_helpers.load_context_from_disk_lwt base_dir context_hash + in + let* acc = + Context.fold context key ~order:`Undefined ~init ~f:(fun path t acc -> + f acc (key @ path) t) + in + let+ () = Tezos_context.Context.close index in + acc + let context_statistics base_dir context_hash = let open Lwt_syntax in let context, index = -- GitLab From bfb6b7a0639497d6c97dcd6085aea6c57644e2f1 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Mon, 2 Sep 2024 16:47:39 +0200 Subject: [PATCH 3/7] Snoop: add config file to auto build --- src/bin_snoop/commands.ml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bin_snoop/commands.ml b/src/bin_snoop/commands.ml index 62b3ae2c9a4b..e4d311fa6a68 100644 --- a/src/bin_snoop/commands.ml +++ b/src/bin_snoop/commands.ml @@ -866,6 +866,7 @@ module Auto_build_cmd = struct destination_directory, nsamples, bench_number, + config_file, print_problem, plot, override_files, @@ -888,6 +889,7 @@ module Auto_build_cmd = struct opts with nsamples = Option.value nsamples ~default:opts.nsamples; bench_number = Option.value bench_number ~default:opts.bench_number; + config_file; } in (split, {destination_directory; infer_parameters; measure_options}) @@ -932,11 +934,12 @@ module Auto_build_cmd = struct () let options = - Tezos_clic.args10 + Tezos_clic.args11 switch destination_directory_arg Benchmark_cmd.Options.nsamples_arg Benchmark_cmd.Options.bench_number_arg + Benchmark_cmd.Options.config_file_arg Infer_cmd.Options.print_problem Infer_cmd.Options.plot_arg Infer_cmd.Options.override_arg -- GitLab From fda600067b4961adde789e52726b50bfb8aba761 Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Mon, 2 Sep 2024 16:48:52 +0200 Subject: [PATCH 4/7] Snoop: move `get_head` to IO helpers --- src/bin_snoop/main_snoop.ml | 21 ++++++--------------- src/lib_shell_benchmarks/io_helpers.ml | 11 +++++++++++ src/lib_shell_benchmarks/io_helpers.mli | 3 +++ 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/bin_snoop/main_snoop.ml b/src/bin_snoop/main_snoop.ml index 5a7bffac78da..108adf0197cc 100644 --- a/src/bin_snoop/main_snoop.ml +++ b/src/bin_snoop/main_snoop.ml @@ -926,25 +926,14 @@ module Auto_build = struct mich_fn ; None) - let get_head_context_hash data_dir = - match - Lwt_main.run @@ Tezos_shell_benchmarks.Io_helpers.load_head_block data_dir - with - | Error e -> - Format.eprintf - "Error: %a@." - Tezos_error_monad.Error_monad.pp_print_trace - e ; - Format.eprintf "Failed to find a Tezos context at %s@." data_dir ; - exit 1 - | Ok res -> res - (* Assumes the data files are found in [_snoop/tezos_node] *) let make_io_read_random_key_benchmark_config dest ns = let open Tezos_shell_benchmarks.Io_benchmarks.Read_random_key_bench in let data_dir = "_snoop/tezos-node" in let context_dir = Filename.concat data_dir "context" in - let level, block_hash, context_hash = get_head_context_hash data_dir in + let level, block_hash, context_hash = + Tezos_shell_benchmarks.Io_helpers.get_head_block_from_context_dir data_dir + in Format.eprintf "Using %s, Context_hash: %a; Block: %ld %a@." context_dir @@ -968,7 +957,9 @@ module Auto_build = struct let open Tezos_shell_benchmarks.Io_benchmarks.Write_random_keys_bench in let data_dir = "_snoop/tezos-node" in let context_dir = Filename.concat data_dir "context" in - let level, block_hash, context_hash = get_head_context_hash data_dir in + let level, block_hash, context_hash = + Tezos_shell_benchmarks.Io_helpers.get_head_block_from_context_dir data_dir + in Format.eprintf "Using %s, Context_hash: %a; Block: %ld %a@." context_dir diff --git a/src/lib_shell_benchmarks/io_helpers.ml b/src/lib_shell_benchmarks/io_helpers.ml index 3e084e6b49d2..b48441190468 100644 --- a/src/lib_shell_benchmarks/io_helpers.ml +++ b/src/lib_shell_benchmarks/io_helpers.ml @@ -513,3 +513,14 @@ let fill_disk_cache ~rng ~restrict_memory context keys_list = let meminfo = Meminfo.get () in Format.eprintf "%a@." Meminfo.pp meminfo ; Lwt.return_unit + +let get_head_block_from_context_dir data_dir = + match Lwt_main.run @@ load_head_block data_dir with + | Error e -> + Format.eprintf + "Error: %a@." + Tezos_error_monad.Error_monad.pp_print_trace + e ; + Format.eprintf "Failed to find a Tezos context at %s@." data_dir ; + exit 1 + | Ok res -> res diff --git a/src/lib_shell_benchmarks/io_helpers.mli b/src/lib_shell_benchmarks/io_helpers.mli index f73e9684228c..6b58f73613b3 100644 --- a/src/lib_shell_benchmarks/io_helpers.mli +++ b/src/lib_shell_benchmarks/io_helpers.mli @@ -170,3 +170,6 @@ val fill_disk_cache : Tezos_protocol_environment.Context.t -> (Tezos_protocol_environment.Context.key * _) array list -> unit Lwt.t + +val get_head_block_from_context_dir : + string -> Int32.t * Block_hash.t * Context_hash.t -- GitLab From 872de6ec7764446f45f5b14bfb59a56ddbaa4e67 Mon Sep 17 00:00:00 2001 From: Jun Furuse Date: Tue, 5 Sep 2023 09:11:45 +0900 Subject: [PATCH 5/7] Shell_benchmarks: adds new io/READ and io/WRITE benchmarks --- .../README_io_benchmarks.md | 82 +++ src/lib_shell_benchmarks/io_benchmarks.ml | 548 ++++++++++++++++-- src/lib_shell_benchmarks/io_benchmarks.mli | 15 + src/lib_shell_benchmarks/io_helpers.ml | 58 +- src/lib_shell_benchmarks/io_helpers.mli | 9 +- 5 files changed, 633 insertions(+), 79 deletions(-) create mode 100644 src/lib_shell_benchmarks/README_io_benchmarks.md diff --git a/src/lib_shell_benchmarks/README_io_benchmarks.md b/src/lib_shell_benchmarks/README_io_benchmarks.md new file mode 100644 index 000000000000..84a67718e5da --- /dev/null +++ b/src/lib_shell_benchmarks/README_io_benchmarks.md @@ -0,0 +1,82 @@ +# New IO benchmarks + +We have 2 new IO benchmarks `io/READ` and `io/WRITE` to get more realistic +gas cost parameters for the storage accesses than the older benchmarks. + +## How to run + +### System requirements + +#### Linux + +The new benchmarks are Linux dependent. They use `/proc` filesystem to +clear the OS's disk cache and retrieve the memory usage information. + +#### Root priviledge + +You need root access of the machine to clear the OS cache. + +### purge_disk_cache.exe + +Source code: `devtools/benchmarks-tools/purge_disk_cache/` + +You must compile `purge_disk_cache.exe` then copy it to the root of +Tezos source code with root setuid flag: + +``` +% dune build devtools/benchmarks-tools/purge_disk_cache/ +% cp _build/default/devtools/benchmarks-tools/purge_disk_cache/purge_disk_cache.exe . +% sudo chown root ./purge_disk_cache.exe +% sudo chmod u+s ./purge_disk_cache.exe +``` + +### Tezos mainnet context + +You need a Tezos data directory somewhere (in this section we will use `_snoop/tezos-node`): + +``` +% ls _snoop/tezos_node +context identity.json peers.json version.json +daily_logs lock store +``` + +For precise benchmarking, the directory should be copied from a running +Tezos mainnet node. It is **not** recommended to use a data directory +freshly imported from a snapshot: + +``` +# OK: the data directory should experience several context GCs +% du -sh _snoop/tezos-node +55G _snoop/tezos-node +``` + +``` +# NG: do not simply import a snapshot +% ./octez-node snapshot import mainnet-xxxxxxx.rolling --data-dir _snoop/tezos-node +% du -sh _snoop/tezos-node +7G _snoop/tezos-node +``` + +The directory `_snoop/tezos-node` should be on an SSD disk without RAID. The disk should have enough free space. + +### Config file + +By default, the data and the cache folders are expected to be found in `_snoop`. +You can redefine these locations with a different configuration. Here is an example of +a minimal template for a config file for this purpose: + +``` +{ "namespace": ".", "config": null, + "children": + [ { "namespace": "io", "config": + { "tezos_data_dir": "/your/data/here/", + "cache_dir": "/your/cache/here/" }, + "children": [] } ] } +``` + +### Benchmark execution + +``` +% rm -rf _io_bench/ +% ./octez-snoop generate code for benchmarks io/READ io/WRITE --out-dir _io_bench/ --config-file config.json +``` diff --git a/src/lib_shell_benchmarks/io_benchmarks.ml b/src/lib_shell_benchmarks/io_benchmarks.ml index a4f55c99cd5a..2f6537ab1db1 100644 --- a/src/lib_shell_benchmarks/io_benchmarks.ml +++ b/src/lib_shell_benchmarks/io_benchmarks.ml @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2020-2021 Nomadic Labs. *) +(* Copyright (c) 2023 DaiLambda, Inc. *) (* Copyright (c) 2023 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) @@ -32,25 +33,45 @@ module Key_map = Io_helpers.Key_map let purpose = Benchmark.Other_purpose "Measuring the time to access context file system" -let ns = Namespace.make Shell_namespace.ns "io" +(* io/s *) +let ns s = Namespace.make Shell_namespace.ns "io" s -let ns2 s1 s2 = Namespace.(cons (cons (Shell_namespace.ns "io") s1) s2) +(* io/s1/s2 *) +let ns2 s1 s2 = (Namespace.make ns s1) s2 let fv s1 s2 = Free_variable.of_namespace (ns2 s1 s2) let read_model ~name = + let fv = fv name in Model.bilinear_affine ~name:(ns2 name "read_model") - ~intercept:(fv name "read_latency") - ~coeff1:(fv name "depth") - ~coeff2:(fv name "storage_bytes_read") + ~intercept:(fv "read_latency") + ~coeff1:(fv "depth") + ~coeff2:(fv "storage_bytes_read") let write_model ~name = + let fv = fv name in Model.bilinear_affine ~name:(ns2 name "write_model") - ~intercept:(fv name "write_latency") - ~coeff1:(fv name "keys_written") - ~coeff2:(fv name "storage_bytes_write") + ~intercept:(fv "write_latency") + ~coeff1:(fv "keys_written") + ~coeff2:(fv "storage_bytes_write") + +let read_model2 ~name = + let fv = fv name in + Model.bilinear_affine + ~name:(ns2 name "read") + ~intercept:(fv "intercept") + ~coeff1:(fv "depth") + ~coeff2:(fv "bytes") + +let write_model2 ~name = + let fv = fv name in + Model.bilinear_affine + ~name:(ns2 name "write") + ~intercept:(fv "intercept") + ~coeff1:(fv "depth") + ~coeff2:(fv "bytes") module Helpers = struct (* Samples keys in an alphabet of [card] elements. *) @@ -65,8 +86,8 @@ module Helpers = struct (* Initializes a context by setting random bytes for each key in the given [key_set]. *) - let random_contents rng_state base_dir index context key_set commit_batch_size - = + let random_contents rng_state context_dir index context key_set + commit_batch_size = let open Lwt_syntax in let* index, context, _ = Key_map.fold_lwt @@ -79,13 +100,13 @@ module Helpers = struct else (* save and proceed with fresh diff *) let* context, index = - Io_helpers.commit_and_reload base_dir index context + Io_helpers.commit_and_reload context_dir index context in Lwt.return (index, context, 0)) key_set (index, context, 0) in - Io_helpers.commit_and_reload base_dir index context + Io_helpers.commit_and_reload context_dir index context let random_key_set rng_state ~depth ~key_card ~insertions = let rec loop remaining acc = @@ -106,20 +127,26 @@ module Helpers = struct in loop insertions initial - let prepare_random_context rng_state base_dir commit_batch_size keys = + let prepare_random_context rng_state context_dir commit_batch_size keys = let context_hash = Io_helpers.assert_ok ~msg:"Io_helpers.prepare_empty_context" - @@ Lwt_main.run (Io_helpers.prepare_empty_context base_dir) + @@ Lwt_main.run (Io_helpers.prepare_empty_context context_dir) in let context, index = - Io_helpers.load_context_from_disk base_dir context_hash + Io_helpers.load_context_from_disk context_dir context_hash in Lwt_main.run (let open Lwt_syntax in let* context, index = - random_contents rng_state base_dir index context keys commit_batch_size + random_contents + rng_state + context_dir + index + context + keys + commit_batch_size in - Io_helpers.commit_and_reload base_dir index context) + Io_helpers.commit_and_reload context_dir index context) end module Context_size_dependent_shared = struct @@ -299,14 +326,14 @@ module Context_size_dependent_read_bench = struct } in let with_context f = - let base_dir = + let context_dir = Filename.temp_file ?temp_dir:cfg.temp_dir (Namespace.basename name) "" in - Io_helpers.prepare_base_dir base_dir ; + Io_helpers.prepare_context_dir context_dir ; let context, index = Helpers.prepare_random_context rng_state - base_dir + context_dir cfg.commit_batch_size keys in @@ -315,7 +342,7 @@ module Context_size_dependent_read_bench = struct Lwt_main.run (let open Lwt_syntax in let* () = Tezos_context.Context.close index in - Tezos_stdlib_unix.Lwt_utils_unix.remove_dir base_dir) + Tezos_stdlib_unix.Lwt_utils_unix.remove_dir context_dir) in let result = try f context @@ -400,14 +427,14 @@ module Context_size_dependent_write_bench = struct } in let with_context f = - let base_dir = + let context_dir = Filename.temp_file ?temp_dir:cfg.temp_dir (Namespace.basename name) "" in - Io_helpers.prepare_base_dir base_dir ; + Io_helpers.prepare_context_dir context_dir ; let context, index = Helpers.prepare_random_context rng_state - base_dir + context_dir cfg.commit_batch_size keys in @@ -418,7 +445,7 @@ module Context_size_dependent_write_bench = struct Lwt_main.run (let open Lwt_syntax in let* () = Tezos_context.Context.close index in - Tezos_stdlib_unix.Lwt_utils_unix.remove_dir base_dir) + Tezos_stdlib_unix.Lwt_utils_unix.remove_dir context_dir) in let result = try f context @@ -676,14 +703,14 @@ module Irmin_pack_read_bench = struct } in let with_context f = - let base_dir = + let context_dir = Filename.temp_file ?temp_dir:cfg.temp_dir (Namespace.basename name) "" in - Io_helpers.prepare_base_dir base_dir ; + Io_helpers.prepare_context_dir context_dir ; let context, index = Helpers.prepare_random_context rng_state - base_dir + context_dir cfg.commit_batch_size keys in @@ -692,7 +719,7 @@ module Irmin_pack_read_bench = struct Lwt_main.run (let open Lwt_syntax in let* () = Tezos_context.Context.close index in - Tezos_stdlib_unix.Lwt_utils_unix.remove_dir base_dir) + Tezos_stdlib_unix.Lwt_utils_unix.remove_dir context_dir) in let result = try f context @@ -833,7 +860,7 @@ module Irmin_pack_write_bench = struct (insertions + total_keys_in_pack) Io_stats.pp stats ; - let base_dir = + let context_dir = Filename.temp_file ?temp_dir:cfg.temp_dir (Namespace.basename name) "" in let value_size = @@ -841,11 +868,11 @@ module Irmin_pack_write_bench = struct * cfg.storage_chunk_bytes in let with_context f = - Io_helpers.prepare_base_dir base_dir ; + Io_helpers.prepare_context_dir context_dir ; let context, index = Helpers.prepare_random_context rng_state - base_dir + context_dir cfg.commit_batch_size key_set in @@ -864,7 +891,7 @@ module Irmin_pack_write_bench = struct Lwt_main.run (let open Lwt_syntax in let* () = Tezos_context.Context.close index in - Tezos_stdlib_unix.Lwt_utils_unix.remove_dir base_dir) + Tezos_stdlib_unix.Lwt_utils_unix.remove_dir context_dir) in let result = try f context @@ -960,8 +987,8 @@ module Read_random_key_bench = struct let key, value_size = keys.(Random.State.int rng_state card) in let with_context f = let context, index = - let base_dir, context_hash = config.existing_context in - Io_helpers.load_context_from_disk base_dir context_hash + let context_dir, context_hash = config.existing_context in + Io_helpers.load_context_from_disk context_dir context_hash in let finalizer () = Gc.compact () ; @@ -990,10 +1017,10 @@ module Read_random_key_bench = struct Generator.With_context {workload; closure; with_context} let create_benchmarks ~rng_state ~bench_num config = - let base_dir, context_hash = config.existing_context in + let context_dir, context_hash = config.existing_context in (* files under [config.subdirectory] *) let tree = - Io_helpers.with_context ~base_dir ~context_hash (fun context -> + Io_helpers.with_context ~context_dir ~context_hash (fun context -> Io_stats.load_tree context @@ Option.value_f ~default:(fun () -> Stdlib.failwith @@ -1126,13 +1153,13 @@ module Write_random_keys_bench = struct let keys_written_to, _keys_not_written_to = Io_helpers.sample_without_replacement number_of_keys_written keys in - let source_base_dir, context_hash = cfg.existing_context in + let source_context_dir, context_hash = cfg.existing_context in let value_size = Base_samplers.sample_in_interval rng_state ~range:cfg.storage_chunks * cfg.storage_chunk_bytes in let with_context f = - let target_base_dir = + let target_context_dir = let temp_dir = Option.value cfg.temp_dir ~default:"/tmp" in Format.asprintf "%s/%s_%d" @@ -1140,13 +1167,15 @@ module Write_random_keys_bench = struct (Namespace.basename name) (Random.int 65536) in - (* copying the original context for EACH test *) - Io_helpers.copy_rec source_base_dir target_base_dir ; - Format.eprintf "Finished copying original context to %s@." target_base_dir ; + (* Copying the original context for EACH test *) + Io_helpers.copy_rec source_context_dir target_context_dir ; + Format.eprintf + "Finished copying original context to %s@." + target_context_dir ; let context, index = - Io_helpers.load_context_from_disk target_base_dir context_hash + Io_helpers.load_context_from_disk target_context_dir context_hash in - (* overwrite [keys_written_to]. The times of the writes are not measured. *) + (* Overwrite [keys_written_to]. The times of the writes are not measured. *) let context = List.fold_left (fun context (key, _) -> @@ -1162,7 +1191,7 @@ module Write_random_keys_bench = struct Lwt_main.run (let open Lwt_syntax in let* () = Tezos_context.Context.close index in - Tezos_stdlib_unix.Lwt_utils_unix.remove_dir target_base_dir) + Tezos_stdlib_unix.Lwt_utils_unix.remove_dir target_context_dir) in let result = try f context @@ -1173,7 +1202,7 @@ module Write_random_keys_bench = struct finalizer () ; result in - (* This only measure the time to commit *) + (* This only measures the time to commit. *) let closure context = Lwt_main.run (let open Lwt_syntax in @@ -1187,10 +1216,10 @@ module Write_random_keys_bench = struct Generator.With_context {workload; closure; with_context} let create_benchmarks ~rng_state ~bench_num config = - let base_dir, context_hash = config.existing_context in - (* files under [config.subdirectory] *) + let context_dir, context_hash = config.existing_context in + (* Files under [config.subdirectory]. *) let tree = - Io_helpers.with_context ~base_dir ~context_hash (fun context -> + Io_helpers.with_context ~context_dir ~context_hash (fun context -> Io_stats.load_tree context @@ Option.value_f ~default:(fun () -> Stdlib.failwith @@ -1205,3 +1234,424 @@ module Write_random_keys_bench = struct end let () = Registration.register_simple_with_num (module Write_random_keys_bench) + +module Shared = struct + let purpose = purpose + + let tags = ["io"] + + let group = Benchmark.Group "io" + + type config = { + tezos_data_dir : string; + cache_dir : string; + memory_available : float; + runs : int; + } + + type data_info = { + data_dir : string; + context_hash : Context_hash.t; + cache_dir : string; + } + + let default_config = + { + tezos_data_dir = "_snoop/tezos-node"; + cache_dir = "_snoop/cache"; + memory_available = 6.0; + runs = 0; + } + + (* [tezos-data-dir]/context *) + let context_dir data_dir = Filename.concat data_dir "context" + + let config_encoding = + let open Data_encoding in + conv + (fun {tezos_data_dir; cache_dir; memory_available; runs} -> + (tezos_data_dir, cache_dir, memory_available, runs)) + (fun (tezos_data_dir, cache_dir, memory_available, runs) -> + {tezos_data_dir; cache_dir; memory_available; runs}) + (obj4 + (req "tezos_data_dir" string) + (req "cache_dir" string) + (req "memory_available" float) + (req "runs" int31)) + + type workload = Key of {depth : int; storage_bytes : int} + + let workload_encoding = + let open Data_encoding in + conv + (function Key {depth; storage_bytes} -> (depth, storage_bytes)) + (fun (depth, storage_bytes) -> Key {depth; storage_bytes}) + (tup2 int31 int31) + + let workload_to_vector = function + | Key {depth; storage_bytes} -> + let keys = + [ + ("depth", float_of_int depth); + ("storage_bytes", float_of_int storage_bytes); + ] + in + Sparse_vec.String.of_list keys + + let make_model model = + Model.make + ~conv:(function + | Key {depth; storage_bytes} -> + (* Shift depth so that it starts from 0 *) + (depth - 1, (storage_bytes, ()))) + model + + (* To avoid long time (≒ 20mins) to traverse the tree, we have a cache file + [/_keysizes.txt] loadable in 3mins *) + let build_key_list data_info = + let open Lwt_result_syntax in + let {cache_dir; context_hash; data_dir} = data_info in + let context_dir = context_dir data_dir in + let*! () = Tezos_stdlib_unix.Lwt_utils_unix.create_dir cache_dir in + let fn_cache = + Filename.concat + cache_dir + (Format.asprintf "%a_keysizes.txt" Context_hash.pp context_hash) + in + if Sys.file_exists fn_cache then return fn_cache + else + let open Lwt_io in + with_file ~mode:Output fn_cache @@ fun oc -> + let*! () = + Io_stats.fold_tree context_dir context_hash [] () (fun () key tree -> + let*! o = Context.Tree.to_value tree in + match o with + | Some bytes -> + let len = Bytes.length bytes in + write_line + oc + (Printf.sprintf "%s %d" (String.concat "/" key) len) + | None -> Lwt.return_unit) + in + let*! () = write_line oc "END OF LIST" in + return fn_cache + + let fold_tree data_info init f = + let fn_cache = + Lwt_main.run @@ build_key_list data_info + |> Result.value_f ~default:(fun _ -> assert false) + in + Format.eprintf + "Loading the cached trees of %s at %s@." + data_info.data_dir + fn_cache ; + let tbl = Stdlib.Hashtbl.create 1024 in + In_channel.with_open_text fn_cache @@ fun ic -> + let rec loop acc = + match input_line ic with + | "END OF LIST" -> acc + | l -> ( + match String.split ' ' ~limit:2 l with + | [k; n] -> + let ks = + let ks = String.split '/' k in + (* hashcons for shorter strings *) + List.map + (fun k -> + if String.length k > 12 then k + else + match Stdlib.Hashtbl.find_opt tbl k with + | Some k -> k + | None -> + Stdlib.Hashtbl.add tbl k k ; + k) + ks + in + loop (f acc (ks, int_of_string n)) + | _ -> + Stdlib.failwith (Printf.sprintf "Broken file list: %s" fn_cache)) + in + loop init + + let stats_keys data_info = + let depths_tbl = Stdlib.Hashtbl.create 101 in + let blocks_tbl = Stdlib.Hashtbl.create 101 in + let nkeys = + let incr tbl key = + let n = Option.value ~default:0 @@ Stdlib.Hashtbl.find_opt tbl key in + Stdlib.Hashtbl.replace tbl key (n + 1) + in + fold_tree data_info 0 (fun nkeys (key, size) -> + let depth = List.length key in + incr depths_tbl depth ; + let blocks = (size + 4095) / 4096 in + incr blocks_tbl blocks ; + nkeys + 1) + in + Format.eprintf "Got %d keys@." nkeys ; + let to_sorted_list tbl = + List.sort (fun (k1, _) (k2, _) -> Int.compare k1 k2) + @@ List.of_seq @@ Stdlib.Hashtbl.to_seq tbl + in + List.iter (fun (depth, n) -> Format.eprintf "Depth %d: %d@." depth n) + @@ to_sorted_list depths_tbl ; + List.iter (fun (blocks, n) -> Format.eprintf "Blocks %d: %d@." blocks n) + @@ to_sorted_list blocks_tbl ; + nkeys + + (* We have nearly 1_000_000_000 files in a recent context and it is impossible + to carry the all in memory. Therefore we get 1_000_000+ random keys from + the context for the benchmark. + + Most of the files are at the directory depth 4, 5 and 6 and smaller than + 4096 bytes. To infer the coefficients of the directory depth and the file + size, the file selection is biased: the files in the directory depth less + than 4 or the files bigger than 4096 are always selected. These special files + are rare: only 23000 are found in today's context. + *) + let sample_keys ~rng data_info = + let nkeys = stats_keys data_info in + let nsamples = 1_000_000 in + + let normals, rares = + let normals, rares = + fold_tree data_info ([], []) (fun (acc, rares) (key, size) -> + let depth = List.length key in + if + size > 4096 (* Big files are rare, so we keep all of them. *) + || depth + <= 3 (* Shallow files are rare, so we keep all of them. *) + then (acc, (key, size) :: rares) + else if Random.State.int rng nkeys < nsamples then + ((key, size) :: acc, rares) + else (acc, rares)) + in + (Array.of_list normals, Array.of_list rares) + in + Format.eprintf + "Got %d normal keys and %d rare keys after filtering@." + (Array.length normals) + (Array.length rares) ; + (normals, rares) + + (* Aggregate the samples + + Samples have lots of noises. To reduce the noise, the samples are grouped + by [value_size]s and the median of each group is used for the data for + [Measure]. + + For the other benchmarks, this denoising is done by [Measure] module which + repeats the run of the same parameter [nsamples] times and takes the median + of the results. In the IO benchmarks we cannot rely on this mechanism due to + the disk cache: the cache hits almost always when the same file is repeatedly + accessed. + *) + let aggregate_samples samples = + let tbl = Stdlib.Hashtbl.create 1023 in + List.iter + (fun (depth, value_size, nsecs) -> + (* We round DOWN them to avoid underestimation *) + let n = value_size / 256 * 256 in + match Stdlib.Hashtbl.find_opt tbl (depth, n) with + | None -> Stdlib.Hashtbl.replace tbl (depth, n) [nsecs] + | Some nsecs_list -> + Stdlib.Hashtbl.replace tbl (depth, n) (nsecs :: nsecs_list)) + samples ; + + (* medians *) + let median xs = + let open Float.Array in + let a = of_list xs in + sort Float.compare a ; + get a (length a / 2) + in + + Stdlib.Hashtbl.fold + (fun (depth, n) nsecs_list acc -> + if List.compare_length_with nsecs_list 5 = -1 then acc + else + let median = median nsecs_list in + let workload = Key {depth; storage_bytes = n} in + (fun () -> + Generator.Calculated {workload; measure = (fun () -> median)}) + :: acc) + tbl + [] + + (* - Use existing context. Mainnet context just before a GC is preferable. + - Restrict the available memory about to 6 GiB, to emulate an 8 GiB machine + - Random accesses to the context to use the available memory for the disk cache. + - Random accesses for the benchmark + + It ignores [bench_num]. + *) + let prepare_io_benchmarks ~rng_state data_info memory_available f = + let {context_hash; data_dir; _} = data_info in + let context_dir = context_dir data_dir in + (* We sample keys in the context, since we cannot carry the all *) + let normal_keys, rare_keys = sample_keys ~rng:rng_state data_info in + + let get_random_key = + let n_normal_keys = Array.length normal_keys in + let n_rare_keys = Array.length rare_keys in + fun () -> + match Random.State.int rng_state 2 with + | 0 -> + let i = Random.State.int rng_state n_normal_keys in + normal_keys.(i) + | _ -> + let i = Random.State.int rng_state n_rare_keys in + rare_keys.(i) + in + (* Actual benchmarks *) + let samples = + Io_helpers.with_memory_restriction + memory_available + (fun restrict_memory -> + restrict_memory () ; + Io_helpers.purge_disk_cache () ; + Io_helpers.with_context ~context_dir ~context_hash (fun context -> + Io_helpers.fill_disk_cache + ~rng:rng_state + ~restrict_memory + context + [normal_keys; rare_keys]) ; + restrict_memory () ; + Lwt_main.run (f ~restrict_memory ~get_random_key)) + in + aggregate_samples samples +end + +module Read_bench = struct + include Shared + + let default_config = {default_config with runs = 1_000_000} + + let name = ns "READ" + + let info = "Benchmarking random read accesses" + + let module_filename = __FILE__ + + let model = make_model (read_model2 ~name:"read") + + let create_benchmarks ~rng_state ~bench_num:_ config = + let context_dir = context_dir config.tezos_data_dir in + let _, _, context_hash = + Io_helpers.get_head_block_from_context_dir config.tezos_data_dir + in + let data_info = + { + data_dir = config.tezos_data_dir; + context_hash; + cache_dir = config.cache_dir; + } + in + prepare_io_benchmarks ~rng_state data_info config.memory_available + @@ fun ~restrict_memory ~get_random_key -> + let open Lwt_syntax in + let* context, index = + Io_helpers.load_context_from_disk_lwt context_dir context_hash + in + let* acc = + let rec loop acc n = + if n <= 0 then Lwt.return acc + else + (* We need flush even for reading. + Otherwise the tree on memory grows forever *) + let* context = Io_helpers.flush context in + let key, value_size = get_random_key () in + let* nsecs, _ = + (* Using [Lwt_main.run] here slows down the benchmark *) + Measure.Time.measure_lwt (fun () -> Context.find context key) + in + let acc = (List.length key, value_size, nsecs) :: acc in + if n mod 10000 = 0 then restrict_memory () ; + loop acc (n - 1) + in + loop [] config.runs + in + let+ () = Tezos_context.Context.close index in + acc +end + +let () = Registration.register_simple_with_num (module Read_bench) + +module Write_bench = struct + include Shared + + let default_config = {default_config with runs = 100_000} + + let name = ns "WRITE" + + let info = "Benchmarking random write accesses" + + let module_filename = __FILE__ + + let model = make_model (write_model2 ~name:"write") + + let create_benchmarks ~rng_state ~bench_num:_ config = + let source_context_dir = context_dir config.tezos_data_dir in + let _, _, context_hash = + Io_helpers.get_head_block_from_context_dir config.tezos_data_dir + in + let data_info = + { + data_dir = config.tezos_data_dir; + context_hash; + cache_dir = config.cache_dir; + } + in + let context_dir = source_context_dir ^ ".tmp" in + prepare_io_benchmarks ~rng_state data_info config.memory_available + @@ fun ~restrict_memory ~get_random_key -> + let open Lwt.Syntax in + (* Copy the context dir *) + let () = + Lwt_main.run @@ Tezos_stdlib_unix.Lwt_utils_unix.remove_dir context_dir + in + Format.eprintf "Copying the data directory to %s@." context_dir ; + Io_helpers.copy_rec source_context_dir context_dir ; + + let* index = Tezos_context.Context.init ~readonly:false context_dir in + let rec loop acc context_hash n = + if n <= 0 then Lwt.return acc + else + let* context = + let+ context = Tezos_context.Context.checkout index context_hash in + match context with + | None -> assert false + | Some context -> + Tezos_shell_context.Shell_context.wrap_disk_context context + in + let key, _value_size = get_random_key () in + (* The biggest file we have is 368640B *) + (* 0B - 4MB *) + let value_size = Random.State.int rng_state 409600 in + + let random_bytes = + Base_samplers.uniform_bytes rng_state ~nbytes:value_size + in + + let* nsecs, context_hash = + (* Using [Lwt_main.run] here slows down the benchmark *) + Measure.Time.measure_lwt (fun () -> + let* context = Context.add context key random_bytes in + let* context_hash = Io_helpers.commit context in + (* We need to call [flush] to finish the disk writing. + It is a sort of the worst case: in a real node, + it is rare to flush just after 1 write. + *) + let+ _context = Io_helpers.flush context in + context_hash) + in + let acc = (List.length key, value_size, nsecs) :: acc in + if n mod 100 = 0 then restrict_memory () ; + loop acc context_hash (n - 1) + in + let* acc = loop [] context_hash config.runs in + let+ () = Tezos_context.Context.close index in + acc +end + +let () = Registration.register_simple_with_num (module Write_bench) diff --git a/src/lib_shell_benchmarks/io_benchmarks.mli b/src/lib_shell_benchmarks/io_benchmarks.mli index 9d3b77442588..bdcbc7843f23 100644 --- a/src/lib_shell_benchmarks/io_benchmarks.mli +++ b/src/lib_shell_benchmarks/io_benchmarks.mli @@ -116,3 +116,18 @@ module Write_random_keys_bench : sig include Benchmark.Simple_with_num with type config := config end + +module Shared : sig + type config = { + tezos_data_dir : string; + cache_dir : string; + memory_available : float; + runs : int; + } + + val config_encoding : config Data_encoding.t +end + +module Read_bench : Benchmark.Simple_with_num with type config := Shared.config + +module Write_bench : Benchmark.Simple_with_num with type config := Shared.config diff --git a/src/lib_shell_benchmarks/io_helpers.ml b/src/lib_shell_benchmarks/io_helpers.ml index b48441190468..d4f5cb982231 100644 --- a/src/lib_shell_benchmarks/io_helpers.ml +++ b/src/lib_shell_benchmarks/io_helpers.ml @@ -30,9 +30,9 @@ let assert_ok ~msg = function Format.eprintf "%s:@.%a@." msg pp_print_trace errs ; exit 1 -let prepare_genesis base_dir = +let prepare_genesis context_dir = let open Lwt_result_syntax in - let*! index = Tezos_context.Context.init ~readonly:false base_dir in + let*! index = Tezos_context.Context.init ~readonly:false context_dir in let genesis_block = Block_hash.of_b58check_exn "BLockGenesisGenesisGenesisGenesisGenesisGeneskvg68z" @@ -67,9 +67,9 @@ let flush context = let+ context = Tezos_context.Context.flush context in Tezos_shell_context.Shell_context.wrap_disk_context context -let prepare_empty_context base_dir = +let prepare_empty_context context_dir = let open Lwt_result_syntax in - let* index, context, _context_hash = prepare_genesis base_dir in + let* index, context, _context_hash = prepare_genesis context_dir in let*! context_hash = commit context in let*! () = Tezos_context.Context.close index in return context_hash @@ -79,11 +79,13 @@ let purge_disk_cache () = let command = "./purge_disk_cache.exe" in match Sys.command command with | 0 -> () - | n -> Format.eprintf "Warning: failed to execute %s: code: %d" command n + | n -> + Stdlib.failwith + (Printf.sprintf "Error: failed to execute %s: code: %d" command n) -let load_context_from_disk_lwt base_dir context_hash = +let load_context_from_disk_lwt context_dir context_hash = let open Lwt_syntax in - let* index = Tezos_context.Context.init ~readonly:false base_dir in + let* index = Tezos_context.Context.init ~readonly:false context_dir in let* o = Tezos_context.Context.checkout index context_hash in match o with | None -> assert false @@ -91,28 +93,28 @@ let load_context_from_disk_lwt base_dir context_hash = Lwt.return (Tezos_shell_context.Shell_context.wrap_disk_context context, index) -let load_context_from_disk base_dir context_hash = - Lwt_main.run (load_context_from_disk_lwt base_dir context_hash) +let load_context_from_disk context_dir context_hash = + Lwt_main.run (load_context_from_disk_lwt context_dir context_hash) -let with_context ~base_dir ~context_hash f = - let context, index = load_context_from_disk base_dir context_hash in +let with_context ~context_dir ~context_hash f = + let context, index = load_context_from_disk context_dir context_hash in Lwt_main.run (let open Lwt_syntax in let* res = f context in let* () = Tezos_context.Context.close index in Lwt.return res) -let prepare_base_dir base_dir = Unix.unlink base_dir +let prepare_context_dir context_dir = Unix.unlink context_dir let initialize_key rng_state context path storage_size = let bytes = Base_samplers.uniform_bytes rng_state ~nbytes:storage_size in Tezos_protocol_environment.Context.add context path bytes -let commit_and_reload base_dir index context = +let commit_and_reload context_dir index context = let open Lwt_syntax in let* context_hash = commit context in let* () = Tezos_context.Context.close index in - load_context_from_disk_lwt base_dir context_hash + load_context_from_disk_lwt context_dir context_hash module Key_map = struct module String_map = String.Map @@ -308,11 +310,10 @@ let rec copy_rec source dest = | _ -> prerr_endline ("Can't cope with special file " ^ source) let split_absolute_path s = - (* Must start with / *) - if s = "" || s.[0] <> '/' then None + if Filename.is_relative s then None else let ss = String.split_no_empty '/' s in - (* Must not contain ., .. *) + (* Must not contain . and .. *) if List.exists (function "." | ".." -> true | _ -> false) ss then None else Some ss @@ -323,9 +324,7 @@ let load_head_block data_dir = let fn_config = Printf.sprintf "%s/store/chain_%s/config.json" data_dir chain_id in - let config = - Lwt_main.run @@ Tezos_stdlib_unix.Lwt_utils_unix.read_file fn_config - in + let config = In_channel.(with_open_text fn_config input_all) in match Data_encoding.Json.from_string config with | Error s -> Stdlib.failwith s | Ok config -> @@ -373,13 +372,12 @@ module Meminfo = struct swapTotal let get () = - let ic = open_in "/proc/meminfo" in + let open In_channel in + with_open_text "/proc/meminfo" @@ fun ic -> let rec loop acc = match input_line ic with - | exception End_of_file -> - close_in ic ; - acc - | s -> + | None -> acc + | Some s -> let acc = try let n = String.index s ':' in @@ -495,10 +493,14 @@ let fill_disk_cache ~rng ~restrict_memory context keys_list = restrict_memory () ; let meminfo = Meminfo.get () in match cond with - | `End_in 0 -> Lwt.return_unit + | `End_in 0 -> + Format.eprintf "Filled the disk cache: (%d KiB cached)@." meminfo.cached ; + Lwt.return_unit | `End_in m -> loop context (`End_in (m - 1)) (n + 1) - | `Caching _ when n >= 20 -> - Format.eprintf "Filling the disk cache: enough tried@." ; + | `Caching _ when n >= 30 -> + Format.eprintf + "Filling the disk cache: enough tried (%d KiB cached)@." + meminfo.cached ; Lwt.return_unit | `Caching _ when meminfo.memAvailable - meminfo.buffers - meminfo.cached diff --git a/src/lib_shell_benchmarks/io_helpers.mli b/src/lib_shell_benchmarks/io_helpers.mli index 6b58f73613b3..8955265e22eb 100644 --- a/src/lib_shell_benchmarks/io_helpers.mli +++ b/src/lib_shell_benchmarks/io_helpers.mli @@ -44,13 +44,18 @@ val prepare_empty_context : string -> (Context_hash.t, tztrace) result Lwt.t val load_context_from_disk : string -> Context_hash.t -> Context.t * Tezos_context.Context.index +val load_context_from_disk_lwt : + string -> + Context_hash.t -> + (Tezos_protocol_environment.Context.t * Tezos_context.Context.index) Lwt.t + val with_context : - base_dir:string -> + context_dir:string -> context_hash:Context_hash.t -> (Context.t -> 'a Lwt.t) -> 'a -val prepare_base_dir : string -> unit +val prepare_context_dir : string -> unit (** This function updates the context with random bytes at a given depth. *) val initialize_key : -- GitLab From cd2f9859b58dbb033d8e26d67296e9f36f40bbbd Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Mon, 2 Sep 2024 16:51:14 +0200 Subject: [PATCH 6/7] Snoop: update codegen check name + def Make it a bit more intuitive --- src/bin_snoop/commands.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin_snoop/commands.ml b/src/bin_snoop/commands.ml index e4d311fa6a68..15d45c817db7 100644 --- a/src/bin_snoop/commands.ml +++ b/src/bin_snoop/commands.ml @@ -844,7 +844,7 @@ module Codegen_check_definitions_cmd = struct let params = Tezos_clic.( - prefixes ["check"; "definitions"; "of"] + prefixes ["check"; "definitions"; "in"; "generated"; "files"] @@ seq_of_param (string ~name:"MLFILE" @@ -854,7 +854,7 @@ module Codegen_check_definitions_cmd = struct Tezos_clic.( command ~group - ~desc:"Check cost functions defined in the given .ml files" + ~desc:"Check cost functions defined in the given generated .ml files" no_options params codegen_check_definitions_handler) -- GitLab From bb1c3fa11c6671c7889bfeba53a5fb0b20c442ec Mon Sep 17 00:00:00 2001 From: Lucas Randazzo Date: Mon, 2 Sep 2024 16:51:58 +0200 Subject: [PATCH 7/7] Snoop: docstring fixes --- src/bin_snoop/main_snoop.ml | 2 +- src/lib_benchmark/benchmark.mli | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/bin_snoop/main_snoop.ml b/src/bin_snoop/main_snoop.ml index 108adf0197cc..6816b952ec2d 100644 --- a/src/bin_snoop/main_snoop.ml +++ b/src/bin_snoop/main_snoop.ml @@ -619,7 +619,7 @@ let codegen_cmd solution_fn model_name codegen_options = stdout_or_file codegen_options.save_to (fun ppf -> Format.fprintf ppf "%a@." Codegen.pp_code code) -(** It returns [(destination, code list) map] *) +(** Returns [(destination, code list) map] *) let generate_code_for_models sol models codegen_options = (* The order of the models is pretty random. It is better to sort them. *) let models = diff --git a/src/lib_benchmark/benchmark.mli b/src/lib_benchmark/benchmark.mli index 6c69d887f8c8..8517d68d4ebd 100644 --- a/src/lib_benchmark/benchmark.mli +++ b/src/lib_benchmark/benchmark.mli @@ -30,7 +30,7 @@ with simpler parameters. Registration functions in {!Registration} will convert these simple interfaces back to [S]. * All the common parameters for these signatures are declared in - [Benchmark_base]. + [Benchmark_base]. *) (** Some benchmarks depend on others, and some are for generic parameters that @@ -47,7 +47,7 @@ * [Generic]: for generic parameters only. *) type group = Standalone | Group of string | Generic -(** Described the purpose of the benchmark. +(** Describes the purpose of the benchmark. * [Generate_code of destination]: generates code at the given [destination] file, prefixed by "src/proto_alpha/lib_protocol/" and suffixed by "_costs_generated.ml". @@ -69,10 +69,7 @@ module type Benchmark_base = sig (** File where the benchmark module is defined *) val module_filename : string - (** Described the purpose of the benchmark. - * [Generate_code of destination]: generates code at the given [destination] file. - * [Other_purpose of purpose]: any other purpose. The goal is to explain why the function is benchmarked since it does not produce a cost function. - *) + (** Describes the purpose of the benchmark. See [purpose] above. *) val purpose : purpose (** Tags of the benchmark *) -- GitLab