diff --git a/src/lib_scoru_wasm/fast/cache.ml b/src/lib_scoru_wasm/fast/cache.ml new file mode 100644 index 0000000000000000000000000000000000000000..115ca879195bc2dac3570266ffad216b7c5d1b85 --- /dev/null +++ b/src/lib_scoru_wasm/fast/cache.ml @@ -0,0 +1,72 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +module type CACHE_INFO = sig + module Key : Stdlib.Hashtbl.HashedType + + type value + + val delete : value -> unit +end + +module Make (Input : CACHE_INFO) = struct + module Table = Hashtbl.Make (Input.Key) + module Fifo = Queue + + type key = Input.Key.t + + type value = Input.value + + let is_in_fifo key = + Fifo.fold (fun acc elt -> acc || Input.Key.equal key elt) false + + type cache = {md : value Table.t; fifo : key Fifo.t; size : int} + + let is_in {fifo; _} key = is_in_fifo key fifo + + let create size = {md = Table.create size; fifo = Fifo.create (); size} + + let find_opt {md; _} = Table.find_opt md + + (* should stay hidden*) + let remove_module cache key = + Option.iter + (fun md -> + Input.delete md ; + Table.remove cache.md key) + (find_opt cache key) + + (* should stay hidden*) + let store_module {md; _} = Table.add md + + let push_key key cache = + Fifo.push key cache.fifo ; + if Fifo.length cache.fifo > cache.size then + remove_module cache (Fifo.pop cache.fifo) + + let replace cache key value = + if is_in cache key then remove_module cache key else push_key key cache ; + store_module cache key value +end diff --git a/src/lib_scoru_wasm/fast/cache.mli b/src/lib_scoru_wasm/fast/cache.mli new file mode 100644 index 0000000000000000000000000000000000000000..b6698148368bcb69c30d810a94f872e687454be6 --- /dev/null +++ b/src/lib_scoru_wasm/fast/cache.mli @@ -0,0 +1,51 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Signature of Cache configuration modules *) +module type CACHE_INFO = sig + module Key : Stdlib.Hashtbl.HashedType + + type value + + (** will be called by the cache each time a value is replaced / removed *) + val delete : value -> unit +end + +(** Functorized Cache *) +module Make (Input : CACHE_INFO) : sig + type key = Input.Key.t + + type value = Input.value + + type cache + + val create : int -> cache + + val find_opt : cache -> key -> value option + + val replace : cache -> key -> value -> unit + + val is_in : cache -> key -> bool +end diff --git a/src/lib_scoru_wasm/fast/exec.ml b/src/lib_scoru_wasm/fast/exec.ml index c5e4feaba457b8fb4afd7254bfead385b7882ea4..bd1d39f94efa8afcd33c7a7f1a9b7b46db94df9c 100644 --- a/src/lib_scoru_wasm/fast/exec.ml +++ b/src/lib_scoru_wasm/fast/exec.ml @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2022 Marigold *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -33,11 +34,8 @@ let store = Wasmer.Store.create engine let load_kernel durable = - let open Lwt.Syntax in - let* kernel = Durable.find_value_exn durable Constants.kernel_key in - let+ kernel = Lazy_containers.Chunked_byte_vector.to_string kernel in let store = Lazy.force store in - Wasmer.Module.(create store Binary kernel) + Module_cache.load_kernel store durable let compute builtins durable buffers = let open Lwt.Syntax in @@ -74,6 +72,8 @@ let compute builtins durable buffers = let* () = kernel_run () in Wasmer.Instance.delete instance ; - Wasmer.Module.delete module_ ; + (* TODO: #4283 + The module is cached, but the cash is never cleaned. + This is the point where it was scrubed before.*) Lwt.return host_state.durable diff --git a/src/lib_scoru_wasm/fast/module_cache.ml b/src/lib_scoru_wasm/fast/module_cache.ml new file mode 100644 index 0000000000000000000000000000000000000000..b964dde92d4268e4b123ebe509af5d5f9bdaf4fc --- /dev/null +++ b/src/lib_scoru_wasm/fast/module_cache.ml @@ -0,0 +1,56 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) +open Tezos_scoru_wasm +module Wasmer = Tezos_wasmer +module Lazy_containers = Tezos_lazy_containers + +module Kernel_cache = Cache.Make (struct + module Key = Tezos_crypto.Context_hash + + type value = Wasmer.Module.t + + let delete = Wasmer.Module.delete +end) + +let kernel_cache = Kernel_cache.create 2 + +let load_parse_module store key durable = + let open Lwt.Syntax in + let* kernel = Durable.find_value_exn durable key in + let+ kernel = Lazy_containers.Chunked_byte_vector.to_string kernel in + Wasmer.Module.(create store Binary kernel) + +let load_module store key durable = + let open Lwt.Syntax in + let* kernel_hash = Durable.hash_exn durable key in + let md = Kernel_cache.find_opt kernel_cache kernel_hash in + match md with + | None -> + let* md = load_parse_module store key durable in + Kernel_cache.replace kernel_cache kernel_hash md ; + Lwt.return md + | Some md -> Lwt.return md + +let load_kernel store durable = load_module store Constants.kernel_key durable diff --git a/src/lib_scoru_wasm/fast/module_cache.mli b/src/lib_scoru_wasm/fast/module_cache.mli new file mode 100644 index 0000000000000000000000000000000000000000..8238b05b0751dc9da64a214102d95add749da800 --- /dev/null +++ b/src/lib_scoru_wasm/fast/module_cache.mli @@ -0,0 +1,32 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +open Tezos_scoru_wasm +module Wasmer = Tezos_wasmer + +(** [load_kernel store durable] returns a module attached to the + Wasmer [store], corresponding to kernel stored in the [durable] storage. + Module is cached, and not reparsed if it has already been instanciated. *) +val load_kernel : Wasmer.Store.t -> Durable.t -> Wasmer.Module.t Lwt.t diff --git a/src/lib_scoru_wasm/test/test_fast_cache.ml b/src/lib_scoru_wasm/test/test_fast_cache.ml new file mode 100644 index 0000000000000000000000000000000000000000..f5c77090916e840c8e500335e1c4986a85e41250 --- /dev/null +++ b/src/lib_scoru_wasm/test/test_fast_cache.ml @@ -0,0 +1,130 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Tezos_scoru_wasm_fast.Module_cache + Invocation: dune exec src/lib_scoru_wasm/test/test_fast_cache.exe + Subject: Test the cache used for Wasmer modules +*) + +module Assert = Lib_test.Assert + +module IntHash = struct + type t = int + + let equal i j = i = j + + let hash i = i land max_int +end + +module Minimal_test_input = struct + module Key = IntHash + + type value = string + + let delete _ = () +end + +module Simple_cache = Tezos_scoru_wasm_fast.Cache.Make (Minimal_test_input) + +(* Tests *) +let test_add_to_empty () = + let cache = Simple_cache.create 2 in + Assert.assert_false "Empty at first" (Simple_cache.is_in cache 0) ; + Simple_cache.replace cache 0 "zero" ; + Assert.assert_true "Not empty anymore" (Simple_cache.is_in cache 0) ; + Lwt_result_syntax.return_unit + +let test_find () = + let cache = Simple_cache.create 2 in + Assert.is_none ~loc:__LOC__ (Simple_cache.find_opt cache 0) ; + Simple_cache.replace cache 0 "zero" ; + Assert.String.Option.equal + ~loc:__LOC__ + (Some "zero") + (Simple_cache.find_opt cache 0) ; + Lwt_result_syntax.return_unit + +let test_replace () = + let removed = ref [] in + let was_removed v = List.mem ~equal:String.equal v !removed in + let module Test_input = struct + module Key = IntHash + + type value = string + + let delete v = removed := v :: !removed + end in + let module Cache = Tezos_scoru_wasm_fast.Cache.Make (Test_input) in + let cache = Cache.create 2 in + Cache.replace cache 0 "zero" ; + Assert.String.Option.equal ~loc:__LOC__ (Some "zero") (Cache.find_opt cache 0) ; + Cache.replace cache 0 "one" ; + Assert.String.Option.equal ~loc:__LOC__ (Some "one") (Cache.find_opt cache 0) ; + Assert.assert_true "zero should have been deleted" (was_removed "zero") ; + Assert.assert_false "one should not have been deleted" (was_removed "one") ; + Lwt_result_syntax.return_unit + +let test_add_max_nb () = + let cache = Simple_cache.create 2 in + Simple_cache.replace cache 0 "zero" ; + Simple_cache.replace cache 1 "one" ; + Assert.assert_true "zero is in" (Simple_cache.is_in cache 0) ; + Assert.assert_true "one is in" (Simple_cache.is_in cache 1) ; + Lwt_result_syntax.return_unit + +let test_add_more_than_max () = + let removed = ref [] in + let was_removed v = List.mem ~equal:String.equal v !removed in + let module Test_input = struct + module Key = IntHash + + type value = string + + let delete v = removed := v :: !removed + end in + let module Cache = Tezos_scoru_wasm_fast.Cache.Make (Test_input) in + let cache = Cache.create 2 in + Assert.assert_false + "value zero should not be marked as deleted" + (was_removed "zero") ; + Cache.replace cache 0 "zero" ; + Cache.replace cache 1 "one" ; + Cache.replace cache 2 "two" ; + Assert.assert_false "zero is out" (Cache.is_in cache 0) ; + Assert.assert_true "zero should have been deleted" (was_removed "zero") ; + Assert.assert_true "one is in" (Cache.is_in cache 1) ; + Assert.assert_true "two is in" (Cache.is_in cache 2) ; + Lwt_result_syntax.return_unit + +let tests = + [ + Tztest.tztest "add to empty" `Quick test_add_to_empty; + Tztest.tztest "Find" `Quick test_find; + Tztest.tztest "Replace existing key" `Quick test_replace; + Tztest.tztest "add max number of keys" `Quick test_add_max_nb; + Tztest.tztest "add more than max" `Quick test_add_more_than_max; + ] diff --git a/src/lib_scoru_wasm/test/test_scoru_wasm.ml b/src/lib_scoru_wasm/test/test_scoru_wasm.ml index d09f9251f5ab55bb88966f65477e6841fb6afaab..6d071aab65304dc44b42ab7bee074c5289b72ec9 100644 --- a/src/lib_scoru_wasm/test/test_scoru_wasm.ml +++ b/src/lib_scoru_wasm/test/test_scoru_wasm.ml @@ -49,5 +49,6 @@ let () = ("Hash correspondence", Test_hash_consistency.tests); ("Reveal", Test_reveal.tests); ("Fast Execution", Test_fast.tests); + ("Fast Execution cache", Test_fast_cache.tests); ] |> Lwt_main.run