[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:
Clemens Backes 2022-09-15 18:55:27 +02:00 committed by V8 LUCI CQ
parent 8436c0059c
commit f9e303e476
8 changed files with 186 additions and 148 deletions

View File

@ -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",

View File

@ -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
View 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
View 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_

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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_