[wasm][pgo] Introduce a separate header and cc file
This moves the existing PGO code to a separate cc file with a separate header. As the implementation will be further extended in follow-up CLs, it's better to have it separated. R=jkummerow@chromium.org Bug: v8:13209 Change-Id: I7b7b5bf9c8d3d542dae734f3874499dccee152a9 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3899321 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/main@{#83250}
This commit is contained in:
parent
8436c0059c
commit
f9e303e476
@ -2527,6 +2527,8 @@ filegroup(
|
||||
"src/wasm/names-provider.cc",
|
||||
"src/wasm/names-provider.h",
|
||||
"src/wasm/object-access.h",
|
||||
"src/wasm/pgo.cc",
|
||||
"src/wasm/pgo.h",
|
||||
"src/wasm/simd-shuffle.cc",
|
||||
"src/wasm/simd-shuffle.h",
|
||||
"src/wasm/stacks.cc",
|
||||
|
2
BUILD.gn
2
BUILD.gn
@ -3625,6 +3625,7 @@ v8_header_set("v8_internal_headers") {
|
||||
"src/wasm/module-instantiate.h",
|
||||
"src/wasm/names-provider.h",
|
||||
"src/wasm/object-access.h",
|
||||
"src/wasm/pgo.h",
|
||||
"src/wasm/simd-shuffle.h",
|
||||
"src/wasm/stacks.h",
|
||||
"src/wasm/streaming-decoder.h",
|
||||
@ -4766,6 +4767,7 @@ v8_source_set("v8_base_without_compiler") {
|
||||
"src/wasm/module-decoder.cc",
|
||||
"src/wasm/module-instantiate.cc",
|
||||
"src/wasm/names-provider.cc",
|
||||
"src/wasm/pgo.cc",
|
||||
"src/wasm/simd-shuffle.cc",
|
||||
"src/wasm/stacks.cc",
|
||||
"src/wasm/streaming-decoder.cc",
|
||||
|
154
src/wasm/pgo.cc
Normal file
154
src/wasm/pgo.cc
Normal file
@ -0,0 +1,154 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/wasm/pgo.h"
|
||||
|
||||
#include "src/wasm/decoder.h"
|
||||
#include "src/wasm/wasm-module-builder.h" // For {ZoneBuffer}.
|
||||
|
||||
namespace v8::internal::wasm {
|
||||
|
||||
base::OwnedVector<uint8_t> GetProfileData(const WasmModule* module) {
|
||||
const TypeFeedbackStorage& type_feedback = module->type_feedback;
|
||||
AccountingAllocator allocator;
|
||||
Zone zone{&allocator, "wasm::GetProfileData"};
|
||||
ZoneBuffer buffer{&zone};
|
||||
base::MutexGuard mutex_guard{&type_feedback.mutex};
|
||||
|
||||
// Get an ordered list of function indexes, so we generate deterministic data.
|
||||
std::vector<uint32_t> ordered_func_indexes;
|
||||
ordered_func_indexes.reserve(type_feedback.feedback_for_function.size());
|
||||
for (const auto& entry : type_feedback.feedback_for_function) {
|
||||
ordered_func_indexes.push_back(entry.first);
|
||||
}
|
||||
std::sort(ordered_func_indexes.begin(), ordered_func_indexes.end());
|
||||
|
||||
buffer.write_u32v(static_cast<uint32_t>(ordered_func_indexes.size()));
|
||||
for (const uint32_t func_index : ordered_func_indexes) {
|
||||
buffer.write_u32v(func_index);
|
||||
// Serialize {feedback_vector}.
|
||||
const FunctionTypeFeedback& feedback =
|
||||
type_feedback.feedback_for_function.at(func_index);
|
||||
buffer.write_u32v(static_cast<uint32_t>(feedback.feedback_vector.size()));
|
||||
for (const CallSiteFeedback& call_site_feedback :
|
||||
feedback.feedback_vector) {
|
||||
int cases = call_site_feedback.num_cases();
|
||||
buffer.write_i32v(cases);
|
||||
for (int i = 0; i < cases; ++i) {
|
||||
buffer.write_i32v(call_site_feedback.function_index(i));
|
||||
buffer.write_i32v(call_site_feedback.call_count(i));
|
||||
}
|
||||
}
|
||||
// Serialize {call_targets}.
|
||||
buffer.write_u32v(static_cast<uint32_t>(feedback.call_targets.size()));
|
||||
for (uint32_t call_target : feedback.call_targets) {
|
||||
buffer.write_u32v(call_target);
|
||||
}
|
||||
}
|
||||
return base::OwnedVector<uint8_t>::Of(buffer);
|
||||
}
|
||||
|
||||
void RestoreProfileData(WasmModule* module,
|
||||
base::Vector<uint8_t> profile_data) {
|
||||
TypeFeedbackStorage& type_feedback = module->type_feedback;
|
||||
Decoder decoder{profile_data.begin(), profile_data.end()};
|
||||
uint32_t num_entries = decoder.consume_u32v("num function entries");
|
||||
CHECK_LE(num_entries, module->num_declared_functions);
|
||||
for (uint32_t missing_entries = num_entries; missing_entries > 0;
|
||||
--missing_entries) {
|
||||
uint32_t function_index = decoder.consume_u32v("function index");
|
||||
CHECK(!type_feedback.feedback_for_function.count(function_index));
|
||||
FunctionTypeFeedback& feedback =
|
||||
type_feedback.feedback_for_function[function_index];
|
||||
// Deserialize {feedback_vector}.
|
||||
uint32_t feedback_vector_size =
|
||||
decoder.consume_u32v("feedback vector size");
|
||||
feedback.feedback_vector.resize(feedback_vector_size);
|
||||
for (CallSiteFeedback& feedback : feedback.feedback_vector) {
|
||||
int num_cases = decoder.consume_i32v("num cases");
|
||||
if (num_cases == 0) continue; // no feedback
|
||||
if (num_cases == 1) { // monomorphic
|
||||
int called_function_index = decoder.consume_i32v("function index");
|
||||
int call_count = decoder.consume_i32v("call count");
|
||||
feedback = CallSiteFeedback{called_function_index, call_count};
|
||||
} else { // polymorphic
|
||||
auto* polymorphic = new CallSiteFeedback::PolymorphicCase[num_cases];
|
||||
for (int i = 0; i < num_cases; ++i) {
|
||||
polymorphic[i].function_index =
|
||||
decoder.consume_i32v("function index");
|
||||
polymorphic[i].absolute_call_frequency =
|
||||
decoder.consume_i32v("call count");
|
||||
}
|
||||
feedback = CallSiteFeedback{polymorphic, num_cases};
|
||||
}
|
||||
}
|
||||
// Deserialize {call_targets}.
|
||||
uint32_t num_call_targets = decoder.consume_u32v("num call targets");
|
||||
feedback.call_targets =
|
||||
base::OwnedVector<uint32_t>::NewForOverwrite(num_call_targets);
|
||||
for (uint32_t& call_target : feedback.call_targets) {
|
||||
call_target = decoder.consume_u32v("call target");
|
||||
}
|
||||
}
|
||||
CHECK(decoder.ok());
|
||||
CHECK_EQ(decoder.pc(), decoder.end());
|
||||
}
|
||||
|
||||
void DumpProfileToFile(const WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes) {
|
||||
CHECK(!wire_bytes.empty());
|
||||
// File are named `profile-wasm-<hash>`.
|
||||
// We use the same hash as for reported scripts, to make it easier to
|
||||
// correlate files to wasm modules (see {CreateWasmScript}).
|
||||
uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
|
||||
base::EmbeddedVector<char, 32> filename;
|
||||
SNPrintF(filename, "profile-wasm-%08x", hash);
|
||||
base::OwnedVector<uint8_t> profile_data = GetProfileData(module);
|
||||
PrintF("Dumping Wasm PGO data to file '%s' (%zu bytes)\n", filename.begin(),
|
||||
profile_data.size());
|
||||
if (FILE* file = base::OS::FOpen(filename.begin(), "wb")) {
|
||||
CHECK_EQ(profile_data.size(),
|
||||
fwrite(profile_data.begin(), 1, profile_data.size(), file));
|
||||
base::Fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadProfileFromFile(WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes) {
|
||||
CHECK(!wire_bytes.empty());
|
||||
// File are named `profile-wasm-<hash>`.
|
||||
// We use the same hash as for reported scripts, to make it easier to
|
||||
// correlate files to wasm modules (see {CreateWasmScript}).
|
||||
uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
|
||||
base::EmbeddedVector<char, 32> filename;
|
||||
SNPrintF(filename, "profile-wasm-%08x", hash);
|
||||
|
||||
FILE* file = base::OS::FOpen(filename.begin(), "rb");
|
||||
if (!file) {
|
||||
PrintF("No Wasm PGO data found: Cannot open file '%s'\n", filename.begin());
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
PrintF("Loading Wasm PGO data from file '%s' (%zu bytes)\n", filename.begin(),
|
||||
size);
|
||||
base::OwnedVector<uint8_t> profile_data =
|
||||
base::OwnedVector<uint8_t>::NewForOverwrite(size);
|
||||
for (size_t read = 0; read < size;) {
|
||||
read += fread(profile_data.begin() + read, 1, size - read, file);
|
||||
CHECK(!ferror(file));
|
||||
}
|
||||
|
||||
base::Fclose(file);
|
||||
|
||||
RestoreProfileData(module, profile_data.as_vector());
|
||||
|
||||
// Check that the generated profile is deterministic.
|
||||
DCHECK_EQ(profile_data.as_vector(), GetProfileData(module).as_vector());
|
||||
}
|
||||
|
||||
} // namespace v8::internal::wasm
|
26
src/wasm/pgo.h
Normal file
26
src/wasm/pgo.h
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2022 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#if !V8_ENABLE_WEBASSEMBLY
|
||||
#error This header should only be included if WebAssembly is enabled.
|
||||
#endif // !V8_ENABLE_WEBASSEMBLY
|
||||
|
||||
#ifndef V8_WASM_PGO_H_
|
||||
#define V8_WASM_PGO_H_
|
||||
|
||||
#include "src/base/vector.h"
|
||||
|
||||
namespace v8::internal::wasm {
|
||||
|
||||
struct WasmModule;
|
||||
|
||||
void DumpProfileToFile(const WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes);
|
||||
|
||||
void LoadProfileFromFile(WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes);
|
||||
|
||||
} // namespace v8::internal::wasm
|
||||
|
||||
#endif // V8_WASM_PGO_H_
|
@ -34,6 +34,7 @@
|
||||
#include "src/wasm/jump-table-assembler.h"
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/names-provider.h"
|
||||
#include "src/wasm/pgo.h"
|
||||
#include "src/wasm/wasm-debug.h"
|
||||
#include "src/wasm/wasm-engine.h"
|
||||
#include "src/wasm/wasm-import-wrapper-cache.h"
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "src/wasm/module-compiler.h"
|
||||
#include "src/wasm/module-decoder.h"
|
||||
#include "src/wasm/module-instantiate.h"
|
||||
#include "src/wasm/pgo.h"
|
||||
#include "src/wasm/stacks.h"
|
||||
#include "src/wasm/streaming-decoder.h"
|
||||
#include "src/wasm/wasm-debug.h"
|
||||
|
@ -678,146 +678,4 @@ size_t GetWireBytesHash(base::Vector<const uint8_t> wire_bytes) {
|
||||
kZeroHashSeed);
|
||||
}
|
||||
|
||||
base::OwnedVector<uint8_t> GetProfileData(const WasmModule* module) {
|
||||
const TypeFeedbackStorage& type_feedback = module->type_feedback;
|
||||
AccountingAllocator allocator;
|
||||
Zone zone{&allocator, "wasm::GetProfileData"};
|
||||
ZoneBuffer buffer{&zone};
|
||||
base::MutexGuard mutex_guard{&type_feedback.mutex};
|
||||
|
||||
// Get an ordered list of function indexes, so we generate deterministic data.
|
||||
std::vector<uint32_t> ordered_func_indexes;
|
||||
ordered_func_indexes.reserve(type_feedback.feedback_for_function.size());
|
||||
for (const auto& entry : type_feedback.feedback_for_function) {
|
||||
ordered_func_indexes.push_back(entry.first);
|
||||
}
|
||||
std::sort(ordered_func_indexes.begin(), ordered_func_indexes.end());
|
||||
|
||||
buffer.write_u32v(static_cast<uint32_t>(ordered_func_indexes.size()));
|
||||
for (const uint32_t func_index : ordered_func_indexes) {
|
||||
buffer.write_u32v(func_index);
|
||||
// Serialize {feedback_vector}.
|
||||
const FunctionTypeFeedback& feedback =
|
||||
type_feedback.feedback_for_function.at(func_index);
|
||||
buffer.write_u32v(static_cast<uint32_t>(feedback.feedback_vector.size()));
|
||||
for (const CallSiteFeedback& call_site_feedback :
|
||||
feedback.feedback_vector) {
|
||||
int cases = call_site_feedback.num_cases();
|
||||
buffer.write_i32v(cases);
|
||||
for (int i = 0; i < cases; ++i) {
|
||||
buffer.write_i32v(call_site_feedback.function_index(i));
|
||||
buffer.write_i32v(call_site_feedback.call_count(i));
|
||||
}
|
||||
}
|
||||
// Serialize {call_targets}.
|
||||
buffer.write_u32v(static_cast<uint32_t>(feedback.call_targets.size()));
|
||||
for (uint32_t call_target : feedback.call_targets) {
|
||||
buffer.write_u32v(call_target);
|
||||
}
|
||||
}
|
||||
return base::OwnedVector<uint8_t>::Of(buffer);
|
||||
}
|
||||
|
||||
void RestoreProfileData(WasmModule* module,
|
||||
base::Vector<uint8_t> profile_data) {
|
||||
TypeFeedbackStorage& type_feedback = module->type_feedback;
|
||||
Decoder decoder{profile_data.begin(), profile_data.end()};
|
||||
uint32_t num_entries = decoder.consume_u32v("num function entries");
|
||||
CHECK_LE(num_entries, module->num_declared_functions);
|
||||
for (uint32_t missing_entries = num_entries; missing_entries > 0;
|
||||
--missing_entries) {
|
||||
uint32_t function_index = decoder.consume_u32v("function index");
|
||||
CHECK(!type_feedback.feedback_for_function.count(function_index));
|
||||
FunctionTypeFeedback& feedback =
|
||||
type_feedback.feedback_for_function[function_index];
|
||||
// Deserialize {feedback_vector}.
|
||||
uint32_t feedback_vector_size =
|
||||
decoder.consume_u32v("feedback vector size");
|
||||
feedback.feedback_vector.resize(feedback_vector_size);
|
||||
for (CallSiteFeedback& feedback : feedback.feedback_vector) {
|
||||
int num_cases = decoder.consume_i32v("num cases");
|
||||
if (num_cases == 0) continue; // no feedback
|
||||
if (num_cases == 1) { // monomorphic
|
||||
int called_function_index = decoder.consume_i32v("function index");
|
||||
int call_count = decoder.consume_i32v("call count");
|
||||
feedback = CallSiteFeedback{called_function_index, call_count};
|
||||
} else { // polymorphic
|
||||
auto* polymorphic = new CallSiteFeedback::PolymorphicCase[num_cases];
|
||||
for (int i = 0; i < num_cases; ++i) {
|
||||
polymorphic[i].function_index =
|
||||
decoder.consume_i32v("function index");
|
||||
polymorphic[i].absolute_call_frequency =
|
||||
decoder.consume_i32v("call count");
|
||||
}
|
||||
feedback = CallSiteFeedback{polymorphic, num_cases};
|
||||
}
|
||||
}
|
||||
// Deserialize {call_targets}.
|
||||
uint32_t num_call_targets = decoder.consume_u32v("num call targets");
|
||||
feedback.call_targets =
|
||||
base::OwnedVector<uint32_t>::NewForOverwrite(num_call_targets);
|
||||
for (uint32_t& call_target : feedback.call_targets) {
|
||||
call_target = decoder.consume_u32v("call target");
|
||||
}
|
||||
}
|
||||
CHECK(decoder.ok());
|
||||
CHECK_EQ(decoder.pc(), decoder.end());
|
||||
}
|
||||
|
||||
void DumpProfileToFile(const WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes) {
|
||||
CHECK(!wire_bytes.empty());
|
||||
// File are named `profile-wasm-<hash>`.
|
||||
// We use the same hash as for reported scripts, to make it easier to
|
||||
// correlate files to wasm modules (see {CreateWasmScript}).
|
||||
uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
|
||||
base::EmbeddedVector<char, 32> filename;
|
||||
SNPrintF(filename, "profile-wasm-%08x", hash);
|
||||
base::OwnedVector<uint8_t> profile_data = GetProfileData(module);
|
||||
PrintF("Dumping Wasm PGO data to file '%s' (%zu bytes)\n", filename.begin(),
|
||||
profile_data.size());
|
||||
if (FILE* file = base::OS::FOpen(filename.begin(), "wb")) {
|
||||
CHECK_EQ(profile_data.size(),
|
||||
fwrite(profile_data.begin(), 1, profile_data.size(), file));
|
||||
base::Fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadProfileFromFile(WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes) {
|
||||
CHECK(!wire_bytes.empty());
|
||||
// File are named `profile-wasm-<hash>`.
|
||||
// We use the same hash as for reported scripts, to make it easier to
|
||||
// correlate files to wasm modules (see {CreateWasmScript}).
|
||||
uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
|
||||
base::EmbeddedVector<char, 32> filename;
|
||||
SNPrintF(filename, "profile-wasm-%08x", hash);
|
||||
|
||||
FILE* file = base::OS::FOpen(filename.begin(), "rb");
|
||||
if (!file) {
|
||||
PrintF("No Wasm PGO data found: Cannot open file '%s'\n", filename.begin());
|
||||
return;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
size_t size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
PrintF("Loading Wasm PGO data from file '%s' (%zu bytes)\n", filename.begin(),
|
||||
size);
|
||||
base::OwnedVector<uint8_t> profile_data =
|
||||
base::OwnedVector<uint8_t>::NewForOverwrite(size);
|
||||
for (size_t read = 0; read < size;) {
|
||||
read += fread(profile_data.begin() + read, 1, size - read, file);
|
||||
CHECK(!ferror(file));
|
||||
}
|
||||
|
||||
base::Fclose(file);
|
||||
|
||||
RestoreProfileData(module, profile_data.as_vector());
|
||||
|
||||
// Check that the generated profile is deterministic.
|
||||
DCHECK_EQ(profile_data.as_vector(), GetProfileData(module).as_vector());
|
||||
}
|
||||
|
||||
} // namespace v8::internal::wasm
|
||||
|
@ -783,12 +783,6 @@ size_t PrintSignature(base::Vector<char> buffer, const wasm::FunctionSig*,
|
||||
V8_EXPORT_PRIVATE size_t
|
||||
GetWireBytesHash(base::Vector<const uint8_t> wire_bytes);
|
||||
|
||||
void DumpProfileToFile(const WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes);
|
||||
|
||||
void LoadProfileFromFile(WasmModule* module,
|
||||
base::Vector<const uint8_t> wire_bytes);
|
||||
|
||||
} // namespace v8::internal::wasm
|
||||
|
||||
#endif // V8_WASM_WASM_MODULE_H_
|
||||
|
Loading…
Reference in New Issue
Block a user