diff --git a/include/proxy-wasm/null_vm.h b/include/proxy-wasm/null_vm.h index a0a3798f..703266df 100644 --- a/include/proxy-wasm/null_vm.h +++ b/include/proxy-wasm/null_vm.h @@ -63,6 +63,8 @@ struct NullVm : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return false; } + void warm() override {} + std::string plugin_name_; std::unique_ptr plugin_; }; diff --git a/include/proxy-wasm/v8.h b/include/proxy-wasm/v8.h index 73c91b95..5531f52c 100644 --- a/include/proxy-wasm/v8.h +++ b/include/proxy-wasm/v8.h @@ -21,6 +21,7 @@ namespace proxy_wasm { +bool initV8Engine(); std::unique_ptr createV8Vm(); } // namespace proxy_wasm diff --git a/include/proxy-wasm/wamr.h b/include/proxy-wasm/wamr.h index 98ff72e3..32a05e94 100644 --- a/include/proxy-wasm/wamr.h +++ b/include/proxy-wasm/wamr.h @@ -21,6 +21,7 @@ namespace proxy_wasm { +bool initWamrEngine(); std::unique_ptr createWamrVm(); } // namespace proxy_wasm diff --git a/include/proxy-wasm/wasm_vm.h b/include/proxy-wasm/wasm_vm.h index db54ebd8..c14624ac 100644 --- a/include/proxy-wasm/wasm_vm.h +++ b/include/proxy-wasm/wasm_vm.h @@ -308,6 +308,11 @@ class WasmVm { */ virtual bool usesWasmByteOrder() = 0; + /** + * Warm the VM such as engine and runtime. + */ + virtual void warm() = 0; + bool isFailed() { return failed_ != FailState::Ok; } void fail(FailState fail_state, std::string_view message) { integration()->error(message); diff --git a/include/proxy-wasm/wasmtime.h b/include/proxy-wasm/wasmtime.h index e3fe4b48..11c4ae7a 100644 --- a/include/proxy-wasm/wasmtime.h +++ b/include/proxy-wasm/wasmtime.h @@ -18,6 +18,7 @@ namespace proxy_wasm { +bool initWasmtimeEngine(); std::unique_ptr createWasmtimeVm(); } // namespace proxy_wasm diff --git a/src/v8/v8.cc b/src/v8/v8.cc index bc5b8285..9e184f29 100644 --- a/src/v8/v8.cc +++ b/src/v8/v8.cc @@ -103,6 +103,8 @@ class V8 : public WasmVm { void terminate() override; bool usesWasmByteOrder() override { return true; } + void warm() override; + private: wasm::own trap(std::string message); @@ -124,6 +126,9 @@ class V8 : public WasmVm { void getModuleFunctionImpl(std::string_view function_name, std::function *function); + // Initialize the V8 engine and store if necessary. + void initStore(); + wasm::own store_; wasm::own module_; wasm::own> shared_module_; @@ -260,9 +265,16 @@ template constexpr T convertValTypesToArgsTuple(const U // V8 implementation. +void V8::initStore() { + if (store_ != nullptr) { + return; + } + store_ = wasm::Store::make(engine()); +} + bool V8::load(std::string_view bytecode, std::string_view precompiled, const std::unordered_map &function_names) { - store_ = wasm::Store::make(engine()); + initStore(); if (store_ == nullptr) { return false; } @@ -708,6 +720,8 @@ void V8::terminate() { isolate->TerminateExecution(); } +void V8::warm() { initStore(); } + std::string V8::getFailMessage(std::string_view function_name, wasm::own trap) { auto message = "Function: " + std::string(function_name) + " failed: "; message += std::string(trap->message().get(), trap->message().size()); @@ -741,6 +755,8 @@ std::string V8::getFailMessage(std::string_view function_name, wasm::own createV8Vm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/src/wamr/wamr.cc b/src/wamr/wamr.cc index 482a59bf..88dc9f00 100644 --- a/src/wamr/wamr.cc +++ b/src/wamr/wamr.cc @@ -90,6 +90,8 @@ class Wamr : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return true; } + void warm() override; + private: template void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, @@ -107,6 +109,9 @@ class Wamr : public WasmVm { void getModuleFunctionImpl(std::string_view function_name, std::function *function); + // Initialize the Wamr store if necessary. + void initStore(); + WasmStorePtr store_; WasmModulePtr module_; WasmSharedModulePtr shared_module_; @@ -119,9 +124,16 @@ class Wamr : public WasmVm { std::unordered_map module_functions_; }; +void Wamr::initStore() { + if (store_ != nullptr) { + return; + } + store_ = wasm_store_new(engine()); +} + bool Wamr::load(std::string_view bytecode, std::string_view precompiled, const std::unordered_map & /*function_names*/) { - store_ = wasm_store_new(engine()); + initStore(); if (store_ == nullptr) { return false; } @@ -697,8 +709,12 @@ void Wamr::getModuleFunctionImpl(std::string_view function_name, }; }; +void Wamr::warm() { initStore(); } + } // namespace wamr +bool initWamrEngine() { return wamr::engine() != nullptr; } + std::unique_ptr createWamrVm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/src/wasmedge/wasmedge.cc b/src/wasmedge/wasmedge.cc index 596af0c9..263ed188 100644 --- a/src/wasmedge/wasmedge.cc +++ b/src/wasmedge/wasmedge.cc @@ -264,6 +264,9 @@ class WasmEdge : public WasmVm { }; FOR_ALL_WASM_VM_EXPORTS(_GET_MODULE_FUNCTION) #undef _GET_MODULE_FUNCTION + + void warm() override; + private: template void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, @@ -284,6 +287,9 @@ class WasmEdge : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return true; } + // Initialize the WasmEdge store if necessary. + void initStore(); + WasmEdgeLoaderPtr loader_; WasmEdgeValidatorPtr validator_; WasmEdgeExecutorPtr executor_; @@ -314,13 +320,18 @@ bool WasmEdge::load(std::string_view bytecode, std::string_view /*precompiled*/, return true; } +void WasmEdge::initStore() { + if (store_ != nullptr) { + return; + } + store_ = WasmEdge_StoreCreate(); +} + bool WasmEdge::link(std::string_view /*debug_name*/) { assert(ast_module_ != nullptr); // Create store and register imports. - if (store_ == nullptr) { - store_ = WasmEdge_StoreCreate(); - } + initStore(); if (store_ == nullptr) { return false; } @@ -609,6 +620,8 @@ void WasmEdge::getModuleFunctionImpl(std::string_view function_name, }; } +void WasmEdge::warm() { initStore(); } + } // namespace WasmEdge std::unique_ptr createWasmEdgeVm() { return std::make_unique(); } diff --git a/src/wasmtime/wasmtime.cc b/src/wasmtime/wasmtime.cc index c4a7646f..ac036162 100644 --- a/src/wasmtime/wasmtime.cc +++ b/src/wasmtime/wasmtime.cc @@ -80,6 +80,9 @@ class Wasmtime : public WasmVm { }; FOR_ALL_WASM_VM_EXPORTS(_GET_MODULE_FUNCTION) #undef _GET_MODULE_FUNCTION + + void warm() override; + private: template void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name, @@ -100,6 +103,9 @@ class Wasmtime : public WasmVm { void terminate() override {} bool usesWasmByteOrder() override { return true; } + // Initialize the Wasmtime store if necessary. + void initStore(); + WasmStorePtr store_; WasmModulePtr module_; WasmSharedModulePtr shared_module_; @@ -111,9 +117,16 @@ class Wasmtime : public WasmVm { std::unordered_map module_functions_; }; +void Wasmtime::initStore() { + if (store_ != nullptr) { + return; + } + store_ = wasm_store_new(engine()); +} + bool Wasmtime::load(std::string_view bytecode, std::string_view /*precompiled*/, const std::unordered_map & /*function_names*/) { - store_ = wasm_store_new(engine()); + initStore(); if (store_ == nullptr) { return false; } @@ -693,8 +706,12 @@ void Wasmtime::getModuleFunctionImpl(std::string_view function_name, }; }; +void Wasmtime::warm() { initStore(); } + } // namespace wasmtime +bool initWasmtimeEngine() { return wasmtime::engine() != nullptr; } + std::unique_ptr createWasmtimeVm() { return std::make_unique(); } } // namespace proxy_wasm diff --git a/test/wasm_vm_test.cc b/test/wasm_vm_test.cc index 4d084cc7..5c7fa09a 100644 --- a/test/wasm_vm_test.cc +++ b/test/wasm_vm_test.cc @@ -30,6 +30,46 @@ INSTANTIATE_TEST_SUITE_P(WasmEngines, TestVm, testing::ValuesIn(getWasmEngines() return info.param; }); +TEST_P(TestVm, Init) { + std::chrono::time_point time2; + + auto time1 = std::chrono::steady_clock::now(); + if (engine_ == "v8") { +#if defined(PROXY_WASM_HOST_ENGINE_V8) + EXPECT_TRUE(proxy_wasm::initV8Engine()); + time2 = std::chrono::steady_clock::now(); + EXPECT_TRUE(proxy_wasm::initV8Engine()); +#endif + } else if (engine_ == "wamr") { +#if defined(PROXY_WASM_HOST_ENGINE_WAMR) + EXPECT_TRUE(proxy_wasm::initWamrEngine()); + time2 = std::chrono::steady_clock::now(); + EXPECT_TRUE(proxy_wasm::initWamrEngine()); +#endif + } else if (engine_ == "wasmtime") { +#if defined(PROXY_WASM_HOST_ENGINE_WASMTIME) + EXPECT_TRUE(proxy_wasm::initWasmtimeEngine()); + time2 = std::chrono::steady_clock::now(); + EXPECT_TRUE(proxy_wasm::initWasmtimeEngine()); +#endif + } else { + return; + } + auto time3 = std::chrono::steady_clock::now(); + + auto cold = std::chrono::duration_cast(time2 - time1).count(); + auto warm = std::chrono::duration_cast(time3 - time2).count(); + + std::cout << "\"cold\" engine time: " << cold << "ns" << std::endl; + std::cout << "\"warm\" engine time: " << warm << "ns" << std::endl; + + // Verify that getting a "warm" engine takes less than 10us. + EXPECT_LE(warm, 10000); + + // Verify that getting a "warm" engine takes at least 50x less time than getting a "cold" one. + EXPECT_LE(warm * 50, cold); +} + TEST_P(TestVm, Basic) { if (engine_ == "wasmedge") { EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::NotCloneable);