v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
pgo.cc
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/wasm/pgo.h"
6
7#include "src/wasm/decoder.h"
8#include "src/wasm/wasm-module-builder.h" // For {ZoneBuffer}.
9
10namespace v8::internal::wasm {
11
12constexpr uint8_t kFunctionExecutedBit = 1 << 0;
13constexpr uint8_t kFunctionTieredUpBit = 1 << 1;
14
16 public:
18 const std::atomic<uint32_t>* tiering_budget_array)
19 : module_(module),
20 type_feedback_mutex_guard_(&module->type_feedback.mutex),
22
24 ZoneBuffer buffer{&zone_};
25
28
29 return base::OwnedCopyOf(buffer);
30 }
31
32 private:
34 const std::unordered_map<uint32_t, FunctionTypeFeedback>&
35 feedback_for_function = module_->type_feedback.feedback_for_function;
36
37 // Get an ordered list of function indexes, so we generate deterministic
38 // data.
39 std::vector<uint32_t> ordered_function_indexes;
40 ordered_function_indexes.reserve(feedback_for_function.size());
41 for (const auto& entry : feedback_for_function) {
42 // Skip functions for which we have no feedback.
43 if (entry.second.feedback_vector.empty()) continue;
44 ordered_function_indexes.push_back(entry.first);
45 }
46 std::sort(ordered_function_indexes.begin(), ordered_function_indexes.end());
47
48 buffer.write_u32v(static_cast<uint32_t>(ordered_function_indexes.size()));
49 for (const uint32_t func_index : ordered_function_indexes) {
50 buffer.write_u32v(func_index);
51 // Serialize {feedback_vector}.
52 const FunctionTypeFeedback& feedback =
53 feedback_for_function.at(func_index);
54 buffer.write_u32v(static_cast<uint32_t>(feedback.feedback_vector.size()));
55 for (const CallSiteFeedback& call_site_feedback :
56 feedback.feedback_vector) {
57 int cases = call_site_feedback.num_cases();
58 buffer.write_i32v(cases);
59 for (int i = 0; i < cases; ++i) {
60 buffer.write_i32v(call_site_feedback.function_index(i));
61 buffer.write_i32v(call_site_feedback.call_count(i));
62 }
63 }
64 // Serialize {call_targets}.
65 buffer.write_u32v(static_cast<uint32_t>(feedback.call_targets.size()));
66 for (uint32_t call_target : feedback.call_targets) {
67 buffer.write_u32v(call_target);
68 }
69 }
70 }
71
73 const std::unordered_map<uint32_t, FunctionTypeFeedback>&
74 feedback_for_function = module_->type_feedback.feedback_for_function;
75 const uint32_t initial_budget = v8_flags.wasm_tiering_budget;
76 for (uint32_t declared_index = 0;
77 declared_index < module_->num_declared_functions; ++declared_index) {
78 uint32_t func_index = declared_index + module_->num_imported_functions;
79 auto feedback_it = feedback_for_function.find(func_index);
80 int prio = feedback_it == feedback_for_function.end()
81 ? 0
82 : feedback_it->second.tierup_priority;
83 DCHECK_LE(0, prio);
84 uint32_t remaining_budget =
85 tiering_budget_array_[declared_index].load(std::memory_order_relaxed);
86 DCHECK_GE(initial_budget, remaining_budget);
87
88 bool was_tiered_up = prio > 0;
89 bool was_executed = was_tiered_up || remaining_budget != initial_budget;
90
91 // TODO(13209): Make this less V8-specific for productionization.
92 buffer.write_u8((was_executed ? kFunctionExecutedBit : 0) |
93 (was_tiered_up ? kFunctionTieredUpBit : 0));
94 }
95 }
96
97 private:
100 Zone zone_{&allocator_, "wasm::ProfileGenerator"};
102 const std::atomic<uint32_t>* const tiering_budget_array_;
103};
104
105void DeserializeTypeFeedback(Decoder& decoder, const WasmModule* module) {
106 base::MutexGuard mutex_guard{&module->type_feedback.mutex};
107 std::unordered_map<uint32_t, FunctionTypeFeedback>& feedback_for_function =
108 module->type_feedback.feedback_for_function;
109 uint32_t num_entries = decoder.consume_u32v("num function entries");
110 CHECK_LE(num_entries, module->num_declared_functions);
111 for (uint32_t missing_entries = num_entries; missing_entries > 0;
112 --missing_entries) {
113 FunctionTypeFeedback function_feedback;
114 uint32_t function_index = decoder.consume_u32v("function index");
115 // Deserialize {feedback_vector}.
116 uint32_t feedback_vector_size =
117 decoder.consume_u32v("feedback vector size");
118 function_feedback.feedback_vector =
120 feedback_vector_size);
121 for (CallSiteFeedback& feedback : function_feedback.feedback_vector) {
122 int num_cases = decoder.consume_i32v("num cases");
123 if (num_cases == 0) continue; // no feedback
124 if (num_cases == 1) { // monomorphic
125 int called_function_index = decoder.consume_i32v("function index");
126 int call_count = decoder.consume_i32v("call count");
127 feedback = CallSiteFeedback{called_function_index, call_count};
128 } else { // polymorphic
129 auto* polymorphic = new CallSiteFeedback::PolymorphicCase[num_cases];
130 for (int i = 0; i < num_cases; ++i) {
131 polymorphic[i].function_index =
132 decoder.consume_i32v("function index");
133 polymorphic[i].absolute_call_frequency =
134 decoder.consume_i32v("call count");
135 }
136 feedback = CallSiteFeedback{polymorphic, num_cases};
137 }
138 }
139 // Deserialize {call_targets}.
140 uint32_t num_call_targets = decoder.consume_u32v("num call targets");
141 function_feedback.call_targets =
143 for (uint32_t& call_target : function_feedback.call_targets) {
144 call_target = decoder.consume_u32v("call target");
145 }
146
147 // Finally, insert the new feedback into the map. Overwrite existing
148 // feedback, but check for consistency.
149 auto [feedback_it, is_new] = feedback_for_function.emplace(
150 function_index, std::move(function_feedback));
151 if (!is_new) {
152 FunctionTypeFeedback& old_feedback = feedback_it->second;
153 CHECK(old_feedback.feedback_vector.empty() ||
154 old_feedback.feedback_vector.size() == feedback_vector_size);
155 CHECK_EQ(old_feedback.call_targets.as_vector(),
156 function_feedback.call_targets.as_vector());
157 std::swap(old_feedback.feedback_vector,
158 function_feedback.feedback_vector);
159 }
160 }
161}
162
163std::unique_ptr<ProfileInformation> DeserializeTieringInformation(
164 Decoder& decoder, const WasmModule* module) {
165 std::vector<uint32_t> executed_functions;
166 std::vector<uint32_t> tiered_up_functions;
167 uint32_t start = module->num_imported_functions;
168 uint32_t end = start + module->num_declared_functions;
169 for (uint32_t func_index = start; func_index < end; ++func_index) {
170 uint8_t tiering_info = decoder.consume_u8("tiering info");
171 CHECK_EQ(0, tiering_info & ~3);
172 bool was_executed = tiering_info & kFunctionExecutedBit;
173 bool was_tiered_up = tiering_info & kFunctionTieredUpBit;
174 if (was_tiered_up) tiered_up_functions.push_back(func_index);
175 if (was_executed) executed_functions.push_back(func_index);
176 }
177
178 return std::make_unique<ProfileInformation>(std::move(executed_functions),
179 std::move(tiered_up_functions));
180}
181
182std::unique_ptr<ProfileInformation> RestoreProfileData(
183 const WasmModule* module, base::Vector<uint8_t> profile_data) {
184 Decoder decoder{profile_data.begin(), profile_data.end()};
185
186 DeserializeTypeFeedback(decoder, module);
187 std::unique_ptr<ProfileInformation> pgo_info =
188 DeserializeTieringInformation(decoder, module);
189
190 CHECK(decoder.ok());
191 CHECK_EQ(decoder.pc(), decoder.end());
192
193 return pgo_info;
194}
195
196void DumpProfileToFile(const WasmModule* module,
198 std::atomic<uint32_t>* tiering_budget_array) {
199 CHECK(!wire_bytes.empty());
200 // File are named `profile-wasm-<hash>`.
201 // We use the same hash as for reported scripts, to make it easier to
202 // correlate files to wasm modules (see {CreateWasmScript}).
203 uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
205 SNPrintF(filename, "profile-wasm-%08x", hash);
206
207 ProfileGenerator profile_generator{module, tiering_budget_array};
208 base::OwnedVector<uint8_t> profile_data = profile_generator.GetProfileData();
209
210 PrintF(
211 "Dumping Wasm PGO data to file '%s' (module size %zu, %u declared "
212 "functions, %zu bytes PGO data)\n",
213 filename.begin(), wire_bytes.size(), module->num_declared_functions,
214 profile_data.size());
215 if (FILE* file = base::OS::FOpen(filename.begin(), "wb")) {
216 size_t written = fwrite(profile_data.begin(), 1, profile_data.size(), file);
217 CHECK_EQ(profile_data.size(), written);
218 base::Fclose(file);
219 }
220}
221
222std::unique_ptr<ProfileInformation> LoadProfileFromFile(
223 const WasmModule* module, base::Vector<const uint8_t> wire_bytes) {
224 CHECK(!wire_bytes.empty());
225 // File are named `profile-wasm-<hash>`.
226 // We use the same hash as for reported scripts, to make it easier to
227 // correlate files to wasm modules (see {CreateWasmScript}).
228 uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(wire_bytes));
230 SNPrintF(filename, "profile-wasm-%08x", hash);
231
232 FILE* file = base::OS::FOpen(filename.begin(), "rb");
233 if (!file) {
234 PrintF("No Wasm PGO data found: Cannot open file '%s'\n", filename.begin());
235 return {};
236 }
237
238 fseek(file, 0, SEEK_END);
239 size_t size = ftell(file);
240 rewind(file);
241
242 PrintF("Loading Wasm PGO data from file '%s' (%zu bytes)\n", filename.begin(),
243 size);
244 base::OwnedVector<uint8_t> profile_data =
246 for (size_t read = 0; read < size;) {
247 read += fread(profile_data.begin() + read, 1, size - read, file);
248 CHECK(!ferror(file));
249 }
250
251 base::Fclose(file);
252
253 return RestoreProfileData(module, profile_data.as_vector());
254}
255
256} // namespace v8::internal::wasm
static FILE * FOpen(const char *path, const char *mode)
constexpr T * begin() const
Definition vector.h:251
constexpr size_t size() const
Definition vector.h:246
Vector< T > as_vector() const
Definition vector.h:276
static OwnedVector< T > NewForOverwrite(size_t size)
Definition vector.h:294
constexpr bool empty() const
Definition vector.h:73
constexpr size_t size() const
Definition vector.h:70
constexpr T * begin() const
Definition vector.h:96
constexpr T * end() const
Definition vector.h:103
int32_t consume_i32v(const char *name="var_int32")
Definition decoder.h:269
const uint8_t * pc() const
Definition decoder.h:408
uint32_t consume_u32v(const char *name="var_uint32")
Definition decoder.h:251
uint8_t consume_u8(const char *name="uint8_t")
Definition decoder.h:225
const uint8_t * end() const
Definition decoder.h:426
void SerializeTypeFeedback(ZoneBuffer &buffer)
Definition pgo.cc:33
ProfileGenerator(const WasmModule *module, const std::atomic< uint32_t > *tiering_budget_array)
Definition pgo.cc:17
const std::atomic< uint32_t > *const tiering_budget_array_
Definition pgo.cc:102
base::OwnedVector< uint8_t > GetProfileData()
Definition pgo.cc:23
const WasmModule * module_
Definition pgo.cc:98
AccountingAllocator allocator_
Definition pgo.cc:99
void SerializeTieringInfo(ZoneBuffer &buffer)
Definition pgo.cc:72
base::MutexGuard type_feedback_mutex_guard_
Definition pgo.cc:101
int start
int end
std::string filename
base::Mutex mutex
int Fclose(FILE *stream)
Definition wrappers.h:22
OwnedVector< T > OwnedCopyOf(const T *data, size_t size)
Definition vector.h:383
void DeserializeTypeFeedback(Decoder &decoder, const WasmModule *module)
Definition pgo.cc:105
constexpr uint8_t kFunctionExecutedBit
Definition pgo.cc:12
constexpr uint8_t kFunctionTieredUpBit
Definition pgo.cc:13
size_t GetWireBytesHash(base::Vector< const uint8_t > wire_bytes)
std::unique_ptr< ProfileInformation > DeserializeTieringInformation(Decoder &decoder, const WasmModule *module)
Definition pgo.cc:163
std::unique_ptr< ProfileInformation > RestoreProfileData(const WasmModule *module, base::Vector< uint8_t > profile_data)
Definition pgo.cc:182
void DumpProfileToFile(const WasmModule *module, base::Vector< const uint8_t > wire_bytes, std::atomic< uint32_t > *tiering_budget_array)
Definition pgo.cc:196
std::unique_ptr< ProfileInformation > LoadProfileFromFile(const WasmModule *module, base::Vector< const uint8_t > wire_bytes)
Definition pgo.cc:222
void PrintF(const char *format,...)
Definition utils.cc:39
kMemory0SizeOffset Address kNewAllocationLimitAddressOffset Address kOldAllocationLimitAddressOffset uint8_t kGlobalsStartOffset kJumpTableStartOffset tiering_budget_array
V8_EXPORT_PRIVATE FlagValues v8_flags
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define CHECK(condition)
Definition logging.h:124
#define CHECK_LE(lhs, rhs)
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define CHECK_EQ(lhs, rhs)
base::OwnedVector< uint32_t > call_targets
base::OwnedVector< CallSiteFeedback > feedback_vector