Commit 15281893 authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

[Liftoff] Compute a better initial assembler buffer size

Instead of always using {AssemblerBase::kMinimalBufferSize}, this CL
computes the expected code size per function compiled with Liftoff, and
uses that size to allocate the initial assembler buffer. This saves
reallocations especially for big functions.

R=jkummerow@chromium.org

Change-Id: I0031033c6be986f9d0d7bb10db0d213669044603
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1910951Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64938}
parent 2c661e44
...@@ -477,8 +477,6 @@ constexpr AssemblerOptions DefaultLiftoffOptions() { ...@@ -477,8 +477,6 @@ constexpr AssemblerOptions DefaultLiftoffOptions() {
} // namespace } // namespace
// TODO(clemensb): Provide a reasonably sized buffer, based on wasm function
// size.
LiftoffAssembler::LiftoffAssembler(std::unique_ptr<AssemblerBuffer> buffer) LiftoffAssembler::LiftoffAssembler(std::unique_ptr<AssemblerBuffer> buffer)
: TurboAssembler(nullptr, DefaultLiftoffOptions(), CodeObjectRequired::kNo, : TurboAssembler(nullptr, DefaultLiftoffOptions(), CodeObjectRequired::kNo,
std::move(buffer)) { std::move(buffer)) {
......
...@@ -2159,17 +2159,22 @@ WasmCompilationResult ExecuteLiftoffCompilation(AccountingAllocator* allocator, ...@@ -2159,17 +2159,22 @@ WasmCompilationResult ExecuteLiftoffCompilation(AccountingAllocator* allocator,
int func_index, int func_index,
Counters* counters, Counters* counters,
WasmFeatures* detected) { WasmFeatures* detected) {
int func_body_size = static_cast<int>(func_body.end - func_body.start);
TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("v8.wasm"), TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"ExecuteLiftoffCompilation", "func_index", func_index, "ExecuteLiftoffCompilation", "func_index", func_index,
"body_size", func_body.end - func_body.start); "body_size", func_body_size);
Zone zone(allocator, "LiftoffCompilationZone"); Zone zone(allocator, "LiftoffCompilationZone");
const WasmModule* module = env ? env->module : nullptr; const WasmModule* module = env ? env->module : nullptr;
auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body.sig); auto call_descriptor = compiler::GetWasmCallDescriptor(&zone, func_body.sig);
base::Optional<TimedHistogramScope> liftoff_compile_time_scope( base::Optional<TimedHistogramScope> liftoff_compile_time_scope(
base::in_place, counters->liftoff_compile_time()); base::in_place, counters->liftoff_compile_time());
size_t code_size_estimate =
WasmCodeManager::EstimateLiftoffCodeSize(func_body_size);
// Allocate the initial buffer a bit bigger to avoid reallocation during code
// generation.
std::unique_ptr<wasm::WasmInstructionBuffer> instruction_buffer = std::unique_ptr<wasm::WasmInstructionBuffer> instruction_buffer =
wasm::WasmInstructionBuffer::New(); wasm::WasmInstructionBuffer::New(128 + code_size_estimate * 4 / 3);
WasmFullDecoder<Decoder::kValidate, LiftoffCompiler> decoder( WasmFullDecoder<Decoder::kValidate, LiftoffCompiler> decoder(
&zone, module, env->enabled_features, detected, func_body, &zone, module, env->enabled_features, detected, func_body,
call_descriptor, env, &zone, instruction_buffer->CreateView()); call_descriptor, env, &zone, instruction_buffer->CreateView());
......
...@@ -57,6 +57,9 @@ class WasmInstructionBufferImpl { ...@@ -57,6 +57,9 @@ class WasmInstructionBufferImpl {
WasmInstructionBufferImpl* const holder_; WasmInstructionBufferImpl* const holder_;
}; };
explicit WasmInstructionBufferImpl(size_t size)
: buffer_(OwnedVector<uint8_t>::New(size)) {}
std::unique_ptr<AssemblerBuffer> CreateView() { std::unique_ptr<AssemblerBuffer> CreateView() {
DCHECK_NOT_NULL(buffer_); DCHECK_NOT_NULL(buffer_);
return std::make_unique<View>(buffer_.as_vector(), this); return std::make_unique<View>(buffer_.as_vector(), this);
...@@ -72,8 +75,7 @@ class WasmInstructionBufferImpl { ...@@ -72,8 +75,7 @@ class WasmInstructionBufferImpl {
private: private:
// The current buffer used to emit code. // The current buffer used to emit code.
OwnedVector<uint8_t> buffer_ = OwnedVector<uint8_t> buffer_;
OwnedVector<uint8_t>::New(AssemblerBase::kMinimalBufferSize);
// While the buffer is grown, we need to temporarily also keep the old buffer // While the buffer is grown, we need to temporarily also keep the old buffer
// alive. // alive.
...@@ -100,10 +102,10 @@ std::unique_ptr<uint8_t[]> WasmInstructionBuffer::ReleaseBuffer() { ...@@ -100,10 +102,10 @@ std::unique_ptr<uint8_t[]> WasmInstructionBuffer::ReleaseBuffer() {
} }
// static // static
std::unique_ptr<WasmInstructionBuffer> WasmInstructionBuffer::New() { std::unique_ptr<WasmInstructionBuffer> WasmInstructionBuffer::New(size_t size) {
return std::unique_ptr<WasmInstructionBuffer>{ return std::unique_ptr<WasmInstructionBuffer>{
reinterpret_cast<WasmInstructionBuffer*>( reinterpret_cast<WasmInstructionBuffer*>(new WasmInstructionBufferImpl(
new WasmInstructionBufferImpl())}; std::max(size_t{AssemblerBase::kMinimalBufferSize}, size)))};
} }
// End of PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl // End of PIMPL interface WasmInstructionBuffer for WasmInstBufferImpl
......
...@@ -35,7 +35,9 @@ class WasmInstructionBuffer final { ...@@ -35,7 +35,9 @@ class WasmInstructionBuffer final {
std::unique_ptr<AssemblerBuffer> CreateView(); std::unique_ptr<AssemblerBuffer> CreateView();
std::unique_ptr<uint8_t[]> ReleaseBuffer(); std::unique_ptr<uint8_t[]> ReleaseBuffer();
static std::unique_ptr<WasmInstructionBuffer> New(); // Allocate a new {WasmInstructionBuffer}. The size is the maximum of {size}
// and {AssemblerBase::kMinimalSize}.
static std::unique_ptr<WasmInstructionBuffer> New(size_t size = 0);
// Override {operator delete} to avoid implicit instantiation of {operator // Override {operator delete} to avoid implicit instantiation of {operator
// delete} with {size_t} argument. The {size_t} argument would be incorrect. // delete} with {size_t} argument. The {size_t} argument would be incorrect.
......
...@@ -1539,6 +1539,64 @@ VirtualMemory WasmCodeManager::TryAllocate(size_t size, void* hint) { ...@@ -1539,6 +1539,64 @@ VirtualMemory WasmCodeManager::TryAllocate(size_t size, void* hint) {
return mem; return mem;
} }
namespace {
// The numbers here are rough estimates, used to calculate the size of the
// initial code reservation and for estimating the amount of external memory
// reported to the GC.
// They do not need to be accurate. Choosing them too small will result in
// separate code spaces being allocated (compile time and runtime overhead),
// choosing them too large results in over-reservation (virtual address space
// only).
// The current numbers have been determined on 2019-11-11 by clemensb@, based
// on one small and one large module compiled from C++ by Emscripten. If in
// doubt, they where chosen slightly larger than required, as over-reservation
// is not a big issue currently.
// Numbers will change when Liftoff or TurboFan evolve, other toolchains are
// used to produce the wasm code, or characteristics of wasm modules on the
// web change. They might require occasional tuning.
// This patch might help to find reasonable numbers for any future adaptation:
// https://crrev.com/c/1910945
#if V8_TARGET_ARCH_X64
constexpr size_t kTurbofanFunctionOverhead = 20;
constexpr size_t kTurbofanCodeSizeMultiplier = 3;
constexpr size_t kLiftoffFunctionOverhead = 60;
constexpr size_t kLiftoffCodeSizeMultiplier = 4;
constexpr size_t kImportSize = 350;
#elif V8_TARGET_ARCH_IA32
constexpr size_t kTurbofanFunctionOverhead = 20;
constexpr size_t kTurbofanCodeSizeMultiplier = 4;
constexpr size_t kLiftoffFunctionOverhead = 60;
constexpr size_t kLiftoffCodeSizeMultiplier = 5;
constexpr size_t kImportSize = 480;
#elif V8_TARGET_ARCH_ARM
constexpr size_t kTurbofanFunctionOverhead = 40;
constexpr size_t kTurbofanCodeSizeMultiplier = 4;
constexpr size_t kLiftoffFunctionOverhead = 108;
constexpr size_t kLiftoffCodeSizeMultiplier = 7;
constexpr size_t kImportSize = 750;
#elif V8_TARGET_ARCH_ARM64
constexpr size_t kTurbofanFunctionOverhead = 60;
constexpr size_t kTurbofanCodeSizeMultiplier = 4;
constexpr size_t kLiftoffFunctionOverhead = 80;
constexpr size_t kLiftoffCodeSizeMultiplier = 7;
constexpr size_t kImportSize = 750;
#else
// Other platforms should add their own estimates if needed. Numbers below are
// the minimum of other architectures.
constexpr size_t kTurbofanFunctionOverhead = 20;
constexpr size_t kTurbofanCodeSizeMultiplier = 3;
constexpr size_t kLiftoffFunctionOverhead = 60;
constexpr size_t kLiftoffCodeSizeMultiplier = 4;
constexpr size_t kImportSize = 350;
#endif
} // namespace
// static
size_t WasmCodeManager::EstimateLiftoffCodeSize(int body_size) {
return kLiftoffFunctionOverhead + kCodeAlignment / 2 +
body_size * kLiftoffCodeSizeMultiplier;
}
// static // static
size_t WasmCodeManager::EstimateNativeModuleCodeSize(const WasmModule* module, size_t WasmCodeManager::EstimateNativeModuleCodeSize(const WasmModule* module,
bool include_liftoff) { bool include_liftoff) {
...@@ -1561,56 +1619,6 @@ size_t WasmCodeManager::EstimateNativeModuleCodeSize(int num_functions, ...@@ -1561,56 +1619,6 @@ size_t WasmCodeManager::EstimateNativeModuleCodeSize(int num_functions,
int num_imported_functions, int num_imported_functions,
int code_section_length, int code_section_length,
bool include_liftoff) { bool include_liftoff) {
// The numbers here are rough estimates, used to calculate the size of the
// initial code reservation and for estimating the amount of external memory
// reported to the GC.
// They do not need to be accurate. Choosing them too small will result in
// separate code spaces being allocated (compile time and runtime overhead),
// choosing them too large results in over-reservation (virtual address space
// only).
// The current numbers have been determined on 2019-11-11 by clemensb@, based
// on one small and one large module compiled from C++ by Emscripten. If in
// doubt, they where chosen slightly larger than required, as over-reservation
// is not a big issue currently.
// Numbers will change when Liftoff or TurboFan evolve, other toolchains are
// used to produce the wasm code, or characteristics of wasm modules on the
// web change. They might require occasional tuning.
// This patch might help to find reasonable numbers for any future adaptation:
// https://crrev.com/c/1910945
#if V8_TARGET_ARCH_X64
constexpr size_t kTurbofanFunctionOverhead = 20;
constexpr size_t kTurbofanCodeSizeMultiplier = 3;
constexpr size_t kLiftoffFunctionOverhead = 60;
constexpr size_t kLiftoffCodeSizeMultiplier = 4;
constexpr size_t kImportSize = 350;
#elif V8_TARGET_ARCH_IA32
constexpr size_t kTurbofanFunctionOverhead = 20;
constexpr size_t kTurbofanCodeSizeMultiplier = 4;
constexpr size_t kLiftoffFunctionOverhead = 60;
constexpr size_t kLiftoffCodeSizeMultiplier = 5;
constexpr size_t kImportSize = 480;
#elif V8_TARGET_ARCH_ARM
constexpr size_t kTurbofanFunctionOverhead = 40;
constexpr size_t kTurbofanCodeSizeMultiplier = 4;
constexpr size_t kLiftoffFunctionOverhead = 108;
constexpr size_t kLiftoffCodeSizeMultiplier = 7;
constexpr size_t kImportSize = 750;
#elif V8_TARGET_ARCH_ARM64
constexpr size_t kTurbofanFunctionOverhead = 60;
constexpr size_t kTurbofanCodeSizeMultiplier = 4;
constexpr size_t kLiftoffFunctionOverhead = 80;
constexpr size_t kLiftoffCodeSizeMultiplier = 7;
constexpr size_t kImportSize = 750;
#else
// Other platforms should add their own estimates if needed. Numbers below are
// the minimum of other architectures.
constexpr size_t kTurbofanFunctionOverhead = 20;
constexpr size_t kTurbofanCodeSizeMultiplier = 3;
constexpr size_t kLiftoffFunctionOverhead = 60;
constexpr size_t kLiftoffCodeSizeMultiplier = 4;
constexpr size_t kImportSize = 350;
#endif
const size_t overhead_per_function = const size_t overhead_per_function =
kTurbofanFunctionOverhead + kCodeAlignment / 2 + kTurbofanFunctionOverhead + kCodeAlignment / 2 +
(include_liftoff ? kLiftoffFunctionOverhead + kCodeAlignment / 2 : 0); (include_liftoff ? kLiftoffFunctionOverhead + kCodeAlignment / 2 : 0);
......
...@@ -692,6 +692,9 @@ class V8_EXPORT_PRIVATE WasmCodeManager final { ...@@ -692,6 +692,9 @@ class V8_EXPORT_PRIVATE WasmCodeManager final {
return total_committed_code_space_.load(); return total_committed_code_space_.load();
} }
// Estimate the needed code space for a Liftoff function based on the size of
// the function body (wasm byte code).
static size_t EstimateLiftoffCodeSize(int body_size);
// Estimate the needed code space from a completely decoded module. // Estimate the needed code space from a completely decoded module.
static size_t EstimateNativeModuleCodeSize(const WasmModule* module, static size_t EstimateNativeModuleCodeSize(const WasmModule* module,
bool include_liftoff); bool include_liftoff);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment