[wasm] Cache the tiering budget with the code
With dynamic tiering, each WebAssembly function has a tiering budget, and the function gets optimized once the tiering budget is reached. So far the tiering budget exists per process, which means that whenever a web application got loaded, it started with a full tiering budget. As a result, functions that only get called few times during startup and never reach the tiering budget would never get optimized. With this CL the tiering budget gets written to the cache. Given that caching events are happening, this means that also startup functions get optimized eventually as long as the web application gets visited often enough. R=clemensb@chromium.org Bug: chromium:1384530 Change-Id: I5066bc8f3daf457159b6eb785d2e17eda43c8c4c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4026769 Commit-Queue: Andreas Haas <ahaas@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/main@{#84320}
This commit is contained in:
parent
145853f5c1
commit
a9e53d6e44
@ -868,7 +868,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
|
||||
// Get or create the NamesProvider. Requires {HasWireBytes()}.
|
||||
NamesProvider* GetNamesProvider();
|
||||
|
||||
uint32_t* tiering_budget_array() { return tiering_budgets_.get(); }
|
||||
uint32_t* tiering_budget_array() const { return tiering_budgets_.get(); }
|
||||
|
||||
Counters* counters() const { return code_allocator_.counters(); }
|
||||
|
||||
|
@ -286,6 +286,7 @@ class V8_EXPORT_PRIVATE NativeModuleSerializer {
|
||||
size_t MeasureCode(const WasmCode*) const;
|
||||
void WriteHeader(Writer*, size_t total_code_size);
|
||||
void WriteCode(const WasmCode*, Writer*);
|
||||
void WriteTieringBudget(Writer* writer);
|
||||
|
||||
const NativeModule* const native_module_;
|
||||
const base::Vector<WasmCode* const> code_table_;
|
||||
@ -318,6 +319,9 @@ size_t NativeModuleSerializer::Measure() const {
|
||||
for (WasmCode* code : code_table_) {
|
||||
size += MeasureCode(code);
|
||||
}
|
||||
// Add the size of the tiering budget.
|
||||
size += native_module_->module()->num_declared_functions * sizeof(uint32_t);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -446,6 +450,12 @@ void NativeModuleSerializer::WriteCode(const WasmCode* code, Writer* writer) {
|
||||
total_written_code_ += code_size;
|
||||
}
|
||||
|
||||
void NativeModuleSerializer::WriteTieringBudget(Writer* writer) {
|
||||
writer->WriteVector(base::Vector<const byte>::cast(
|
||||
base::VectorOf(native_module_->tiering_budget_array(),
|
||||
native_module_->module()->num_declared_functions)));
|
||||
}
|
||||
|
||||
bool NativeModuleSerializer::Write(Writer* writer) {
|
||||
DCHECK(!write_called_);
|
||||
write_called_ = true;
|
||||
@ -468,6 +478,7 @@ bool NativeModuleSerializer::Write(Writer* writer) {
|
||||
// Make sure that the serialized total code size was correct.
|
||||
CHECK_EQ(total_written_code_, total_code_size);
|
||||
|
||||
WriteTieringBudget(writer);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -561,6 +572,7 @@ class V8_EXPORT_PRIVATE NativeModuleDeserializer {
|
||||
|
||||
void ReadHeader(Reader* reader);
|
||||
DeserializationUnit ReadCode(int fn_index, Reader* reader);
|
||||
void ReadTieringBudget(Reader* reader);
|
||||
void CopyAndRelocate(const DeserializationUnit& unit);
|
||||
void Publish(std::vector<DeserializationUnit> batch);
|
||||
|
||||
@ -700,6 +712,7 @@ bool NativeModuleDeserializer::Read(Reader* reader) {
|
||||
// Wait for all tasks to finish, while participating in their work.
|
||||
job_handle->Join();
|
||||
|
||||
ReadTieringBudget(reader);
|
||||
return reader->current_size() == 0;
|
||||
}
|
||||
|
||||
@ -823,6 +836,19 @@ void NativeModuleDeserializer::CopyAndRelocate(
|
||||
unit.code->instructions().size());
|
||||
}
|
||||
|
||||
void NativeModuleDeserializer::ReadTieringBudget(Reader* reader) {
|
||||
size_t size_of_tiering_budget =
|
||||
native_module_->module()->num_declared_functions * sizeof(uint32_t);
|
||||
if (size_of_tiering_budget > reader->current_size()) {
|
||||
return;
|
||||
}
|
||||
base::Vector<const byte> serialized_budget =
|
||||
reader->ReadVector<const byte>(size_of_tiering_budget);
|
||||
|
||||
memcpy(native_module_->tiering_budget_array(), serialized_budget.begin(),
|
||||
size_of_tiering_budget);
|
||||
}
|
||||
|
||||
void NativeModuleDeserializer::Publish(std::vector<DeserializationUnit> batch) {
|
||||
DCHECK(!batch.empty());
|
||||
std::vector<std::unique_ptr<WasmCode>> codes;
|
||||
|
@ -39,9 +39,14 @@ class WasmSerializationTest {
|
||||
WasmModuleBuilder* builder = zone->New<WasmModuleBuilder>(zone);
|
||||
TestSignatures sigs;
|
||||
|
||||
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_i());
|
||||
byte code[] = {WASM_LOCAL_GET(0), kExprI32Const, 1, kExprI32Add, kExprEnd};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
// Generate 3 functions, and export the last one with the name "increment".
|
||||
WasmFunctionBuilder* f;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
f = builder->AddFunction(sigs.i_i());
|
||||
byte code[] = {WASM_LOCAL_GET(0), kExprI32Const, 1, kExprI32Add,
|
||||
kExprEnd};
|
||||
f->EmitCode(code, sizeof(code));
|
||||
}
|
||||
builder->AddExport(base::CStrVector(kFunctionName), f);
|
||||
|
||||
builder->WriteTo(buffer);
|
||||
@ -60,6 +65,11 @@ class WasmSerializationTest {
|
||||
memset(const_cast<uint8_t*>(wire_bytes_.data()), 0, wire_bytes_.size() / 2);
|
||||
}
|
||||
|
||||
void PartlyDropTieringBudget() {
|
||||
serialized_bytes_ = {serialized_bytes_.data(),
|
||||
serialized_bytes_.size() - 1};
|
||||
}
|
||||
|
||||
MaybeHandle<WasmModuleObject> Deserialize(
|
||||
base::Vector<const char> source_url = {}) {
|
||||
return DeserializeNativeModule(CcTest::i_isolate(),
|
||||
@ -99,6 +109,8 @@ class WasmSerializationTest {
|
||||
CcTest::CollectAllAvailableGarbage();
|
||||
}
|
||||
|
||||
v8::MemorySpan<const uint8_t> wire_bytes() const { return wire_bytes_; }
|
||||
|
||||
private:
|
||||
Zone* zone() { return &zone_; }
|
||||
|
||||
@ -322,11 +334,11 @@ TEST(TierDownAfterDeserialization) {
|
||||
CHECK(test.Deserialize().ToHandle(&module_object));
|
||||
|
||||
auto* native_module = module_object->native_module();
|
||||
CHECK_EQ(1, native_module->module()->functions.size());
|
||||
CHECK_EQ(3, native_module->module()->functions.size());
|
||||
WasmCodeRefScope code_ref_scope;
|
||||
// The deserialized code must be TurboFan (we wait for tier-up before
|
||||
// serializing).
|
||||
auto* turbofan_code = native_module->GetCode(0);
|
||||
auto* turbofan_code = native_module->GetCode(2);
|
||||
CHECK_NOT_NULL(turbofan_code);
|
||||
CHECK_EQ(ExecutionTier::kTurbofan, turbofan_code->tier());
|
||||
|
||||
@ -366,4 +378,54 @@ TEST(SerializeLiftoffModuleFails) {
|
||||
CHECK(!wasm_serializer.SerializeNativeModule({buffer.get(), buffer_size}));
|
||||
}
|
||||
|
||||
TEST(SerializeTieringBudget) {
|
||||
WasmSerializationTest test;
|
||||
|
||||
Isolate* isolate = CcTest::i_isolate();
|
||||
v8::OwnedBuffer serialized_bytes;
|
||||
uint32_t mock_budget[3]{1, 2, 3};
|
||||
{
|
||||
HandleScope scope(isolate);
|
||||
Handle<WasmModuleObject> module_object;
|
||||
CHECK(test.Deserialize().ToHandle(&module_object));
|
||||
|
||||
auto* native_module = module_object->native_module();
|
||||
memcpy(native_module->tiering_budget_array(), mock_budget,
|
||||
arraysize(mock_budget) * sizeof(uint32_t));
|
||||
v8::Local<v8::Object> v8_module_obj =
|
||||
v8::Utils::ToLocal(Handle<JSObject>::cast(module_object));
|
||||
CHECK(v8_module_obj->IsWasmModuleObject());
|
||||
|
||||
v8::Local<v8::WasmModuleObject> v8_module_object =
|
||||
v8_module_obj.As<v8::WasmModuleObject>();
|
||||
serialized_bytes = v8_module_object->GetCompiledModule().Serialize();
|
||||
|
||||
// Change one entry in the tiering budget after serialization to make sure
|
||||
// the module gets deserialized and not just loaded from the module cache.
|
||||
native_module->tiering_budget_array()[0]++;
|
||||
}
|
||||
test.CollectGarbage();
|
||||
HandleScope scope(isolate);
|
||||
Handle<WasmModuleObject> module_object;
|
||||
CHECK(DeserializeNativeModule(isolate,
|
||||
base::VectorOf(serialized_bytes.buffer.get(),
|
||||
serialized_bytes.size),
|
||||
base::VectorOf(test.wire_bytes()), {})
|
||||
.ToHandle(&module_object));
|
||||
|
||||
auto* native_module = module_object->native_module();
|
||||
for (size_t i = 0; i < arraysize(mock_budget); ++i) {
|
||||
CHECK_EQ(mock_budget[i], native_module->tiering_budget_array()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DeserializeTieringBudgetPartlyMissing) {
|
||||
WasmSerializationTest test;
|
||||
{
|
||||
HandleScope scope(CcTest::i_isolate());
|
||||
test.PartlyDropTieringBudget();
|
||||
CHECK(test.Deserialize().is_null());
|
||||
}
|
||||
test.CollectGarbage();
|
||||
}
|
||||
} // namespace v8::internal::wasm
|
||||
|
Loading…
Reference in New Issue
Block a user