Skip to content

Commit ee4dfe6

Browse files
authored
Feat: Support creating warmed VM in the proxy wasm V8 VM API. (#447)
* Support creating warmed VM in the proxy wasm V8 VM API. Signed-off-by: Rachel Green <[email protected]>
1 parent cbc2dd9 commit ee4dfe6

File tree

10 files changed

+126
-6
lines changed

10 files changed

+126
-6
lines changed

include/proxy-wasm/null_vm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ struct NullVm : public WasmVm {
6363
void terminate() override {}
6464
bool usesWasmByteOrder() override { return false; }
6565

66+
void warm() override {}
67+
6668
std::string plugin_name_;
6769
std::unique_ptr<NullVmPlugin> plugin_;
6870
};

include/proxy-wasm/v8.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
namespace proxy_wasm {
2323

24+
bool initV8Engine();
2425
std::unique_ptr<WasmVm> createV8Vm();
2526

2627
} // namespace proxy_wasm

include/proxy-wasm/wamr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
namespace proxy_wasm {
2323

24+
bool initWamrEngine();
2425
std::unique_ptr<WasmVm> createWamrVm();
2526

2627
} // namespace proxy_wasm

include/proxy-wasm/wasm_vm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,11 @@ class WasmVm {
308308
*/
309309
virtual bool usesWasmByteOrder() = 0;
310310

311+
/**
312+
* Warm the VM such as engine and runtime.
313+
*/
314+
virtual void warm() = 0;
315+
311316
bool isFailed() { return failed_ != FailState::Ok; }
312317
void fail(FailState fail_state, std::string_view message) {
313318
integration()->error(message);

include/proxy-wasm/wasmtime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
namespace proxy_wasm {
2020

21+
bool initWasmtimeEngine();
2122
std::unique_ptr<WasmVm> createWasmtimeVm();
2223

2324
} // namespace proxy_wasm

src/v8/v8.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ class V8 : public WasmVm {
103103
void terminate() override;
104104
bool usesWasmByteOrder() override { return true; }
105105

106+
void warm() override;
107+
106108
private:
107109
wasm::own<wasm::Trap> trap(std::string message);
108110

@@ -124,6 +126,9 @@ class V8 : public WasmVm {
124126
void getModuleFunctionImpl(std::string_view function_name,
125127
std::function<R(ContextBase *, Args...)> *function);
126128

129+
// Initialize the V8 engine and store if necessary.
130+
void initStore();
131+
127132
wasm::own<wasm::Store> store_;
128133
wasm::own<wasm::Module> module_;
129134
wasm::own<wasm::Shared<wasm::Module>> shared_module_;
@@ -260,9 +265,16 @@ template <typename T, typename U> constexpr T convertValTypesToArgsTuple(const U
260265

261266
// V8 implementation.
262267

268+
void V8::initStore() {
269+
if (store_ != nullptr) {
270+
return;
271+
}
272+
store_ = wasm::Store::make(engine());
273+
}
274+
263275
bool V8::load(std::string_view bytecode, std::string_view precompiled,
264276
const std::unordered_map<uint32_t, std::string> &function_names) {
265-
store_ = wasm::Store::make(engine());
277+
initStore();
266278
if (store_ == nullptr) {
267279
return false;
268280
}
@@ -708,6 +720,8 @@ void V8::terminate() {
708720
isolate->TerminateExecution();
709721
}
710722

723+
void V8::warm() { initStore(); }
724+
711725
std::string V8::getFailMessage(std::string_view function_name, wasm::own<wasm::Trap> trap) {
712726
auto message = "Function: " + std::string(function_name) + " failed: ";
713727
message += std::string(trap->message().get(), trap->message().size());
@@ -741,6 +755,8 @@ std::string V8::getFailMessage(std::string_view function_name, wasm::own<wasm::T
741755

742756
} // namespace v8
743757

758+
bool initV8Engine() { return v8::engine() != nullptr; }
759+
744760
std::unique_ptr<WasmVm> createV8Vm() { return std::make_unique<v8::V8>(); }
745761

746762
} // namespace proxy_wasm

src/wamr/wamr.cc

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ class Wamr : public WasmVm {
9090
void terminate() override {}
9191
bool usesWasmByteOrder() override { return true; }
9292

93+
void warm() override;
94+
9395
private:
9496
template <typename... Args>
9597
void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name,
@@ -107,6 +109,9 @@ class Wamr : public WasmVm {
107109
void getModuleFunctionImpl(std::string_view function_name,
108110
std::function<R(ContextBase *, Args...)> *function);
109111

112+
// Initialize the Wamr store if necessary.
113+
void initStore();
114+
110115
WasmStorePtr store_;
111116
WasmModulePtr module_;
112117
WasmSharedModulePtr shared_module_;
@@ -119,9 +124,16 @@ class Wamr : public WasmVm {
119124
std::unordered_map<std::string, WasmFuncPtr> module_functions_;
120125
};
121126

127+
void Wamr::initStore() {
128+
if (store_ != nullptr) {
129+
return;
130+
}
131+
store_ = wasm_store_new(engine());
132+
}
133+
122134
bool Wamr::load(std::string_view bytecode, std::string_view precompiled,
123135
const std::unordered_map<uint32_t, std::string> & /*function_names*/) {
124-
store_ = wasm_store_new(engine());
136+
initStore();
125137
if (store_ == nullptr) {
126138
return false;
127139
}
@@ -697,8 +709,12 @@ void Wamr::getModuleFunctionImpl(std::string_view function_name,
697709
};
698710
};
699711

712+
void Wamr::warm() { initStore(); }
713+
700714
} // namespace wamr
701715

716+
bool initWamrEngine() { return wamr::engine() != nullptr; }
717+
702718
std::unique_ptr<WasmVm> createWamrVm() { return std::make_unique<wamr::Wamr>(); }
703719

704720
} // namespace proxy_wasm

src/wasmedge/wasmedge.cc

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ class WasmEdge : public WasmVm {
264264
};
265265
FOR_ALL_WASM_VM_EXPORTS(_GET_MODULE_FUNCTION)
266266
#undef _GET_MODULE_FUNCTION
267+
268+
void warm() override;
269+
267270
private:
268271
template <typename... Args>
269272
void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name,
@@ -284,6 +287,9 @@ class WasmEdge : public WasmVm {
284287
void terminate() override {}
285288
bool usesWasmByteOrder() override { return true; }
286289

290+
// Initialize the WasmEdge store if necessary.
291+
void initStore();
292+
287293
WasmEdgeLoaderPtr loader_;
288294
WasmEdgeValidatorPtr validator_;
289295
WasmEdgeExecutorPtr executor_;
@@ -314,13 +320,18 @@ bool WasmEdge::load(std::string_view bytecode, std::string_view /*precompiled*/,
314320
return true;
315321
}
316322

323+
void WasmEdge::initStore() {
324+
if (store_ != nullptr) {
325+
return;
326+
}
327+
store_ = WasmEdge_StoreCreate();
328+
}
329+
317330
bool WasmEdge::link(std::string_view /*debug_name*/) {
318331
assert(ast_module_ != nullptr);
319332

320333
// Create store and register imports.
321-
if (store_ == nullptr) {
322-
store_ = WasmEdge_StoreCreate();
323-
}
334+
initStore();
324335
if (store_ == nullptr) {
325336
return false;
326337
}
@@ -609,6 +620,8 @@ void WasmEdge::getModuleFunctionImpl(std::string_view function_name,
609620
};
610621
}
611622

623+
void WasmEdge::warm() { initStore(); }
624+
612625
} // namespace WasmEdge
613626

614627
std::unique_ptr<WasmVm> createWasmEdgeVm() { return std::make_unique<WasmEdge::WasmEdge>(); }

src/wasmtime/wasmtime.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class Wasmtime : public WasmVm {
8080
};
8181
FOR_ALL_WASM_VM_EXPORTS(_GET_MODULE_FUNCTION)
8282
#undef _GET_MODULE_FUNCTION
83+
84+
void warm() override;
85+
8386
private:
8487
template <typename... Args>
8588
void registerHostFunctionImpl(std::string_view module_name, std::string_view function_name,
@@ -100,6 +103,9 @@ class Wasmtime : public WasmVm {
100103
void terminate() override {}
101104
bool usesWasmByteOrder() override { return true; }
102105

106+
// Initialize the Wasmtime store if necessary.
107+
void initStore();
108+
103109
WasmStorePtr store_;
104110
WasmModulePtr module_;
105111
WasmSharedModulePtr shared_module_;
@@ -111,9 +117,16 @@ class Wasmtime : public WasmVm {
111117
std::unordered_map<std::string, WasmFuncPtr> module_functions_;
112118
};
113119

120+
void Wasmtime::initStore() {
121+
if (store_ != nullptr) {
122+
return;
123+
}
124+
store_ = wasm_store_new(engine());
125+
}
126+
114127
bool Wasmtime::load(std::string_view bytecode, std::string_view /*precompiled*/,
115128
const std::unordered_map<uint32_t, std::string> & /*function_names*/) {
116-
store_ = wasm_store_new(engine());
129+
initStore();
117130
if (store_ == nullptr) {
118131
return false;
119132
}
@@ -693,8 +706,12 @@ void Wasmtime::getModuleFunctionImpl(std::string_view function_name,
693706
};
694707
};
695708

709+
void Wasmtime::warm() { initStore(); }
710+
696711
} // namespace wasmtime
697712

713+
bool initWasmtimeEngine() { return wasmtime::engine() != nullptr; }
714+
698715
std::unique_ptr<WasmVm> createWasmtimeVm() { return std::make_unique<wasmtime::Wasmtime>(); }
699716

700717
} // namespace proxy_wasm

test/wasm_vm_test.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,54 @@ INSTANTIATE_TEST_SUITE_P(WasmEngines, TestVm, testing::ValuesIn(getWasmEngines()
3030
return info.param;
3131
});
3232

33+
TEST_P(TestVm, Init) {
34+
std::chrono::time_point<std::chrono::steady_clock> time2;
35+
36+
auto time1 = std::chrono::steady_clock::now();
37+
if (engine_ == "v8") {
38+
#if defined(PROXY_WASM_HOST_ENGINE_V8)
39+
EXPECT_TRUE(proxy_wasm::initV8Engine());
40+
time2 = std::chrono::steady_clock::now();
41+
EXPECT_TRUE(proxy_wasm::initV8Engine());
42+
#endif
43+
} else if (engine_ == "wamr") {
44+
#if defined(PROXY_WASM_HOST_ENGINE_WAMR)
45+
EXPECT_TRUE(proxy_wasm::initWamrEngine());
46+
time2 = std::chrono::steady_clock::now();
47+
EXPECT_TRUE(proxy_wasm::initWamrEngine());
48+
#endif
49+
} else if (engine_ == "wasmtime") {
50+
#if defined(PROXY_WASM_HOST_ENGINE_WASMTIME)
51+
EXPECT_TRUE(proxy_wasm::initWasmtimeEngine());
52+
time2 = std::chrono::steady_clock::now();
53+
EXPECT_TRUE(proxy_wasm::initWasmtimeEngine());
54+
#endif
55+
} else {
56+
return;
57+
}
58+
auto time3 = std::chrono::steady_clock::now();
59+
60+
auto cold = std::chrono::duration_cast<std::chrono::nanoseconds>(time2 - time1).count();
61+
auto warm = std::chrono::duration_cast<std::chrono::nanoseconds>(time3 - time2).count();
62+
63+
std::cout << "\"cold\" engine time: " << cold << "ns" << std::endl;
64+
std::cout << "\"warm\" engine time: " << warm << "ns" << std::endl;
65+
66+
// Default warm time in nanoseconds.
67+
int warm_time_ns_limit = 10000;
68+
69+
#if defined(__linux__) && defined(__s390x__)
70+
// Linux 390x is significantly slower, so we use a more lenient limit.
71+
warm_time_ns_limit = 75000;
72+
#endif
73+
74+
// Verify that getting a "warm" engine takes less than 10us.
75+
EXPECT_LE(warm, warm_time_ns_limit);
76+
77+
// Verify that getting a "warm" engine takes at least 50x less time than getting a "cold" one.
78+
EXPECT_LE(warm * 50, cold);
79+
}
80+
3381
TEST_P(TestVm, Basic) {
3482
if (engine_ == "wasmedge") {
3583
EXPECT_EQ(vm_->cloneable(), proxy_wasm::Cloneable::NotCloneable);

0 commit comments

Comments
 (0)