Limit size of first page based on serialized data.
R=hpayer@chromium.org BUG=chromium:453111 LOG=Y Review URL: https://codereview.chromium.org/932823002 Cr-Commit-Position: refs/heads/master@{#26764}
This commit is contained in:
parent
c7810004bb
commit
afb2706103
57
src/api.cc
57
src/api.cc
@ -28,7 +28,6 @@
|
||||
#include "src/debug.h"
|
||||
#include "src/deoptimizer.h"
|
||||
#include "src/execution.h"
|
||||
#include "src/full-codegen.h"
|
||||
#include "src/global-handles.h"
|
||||
#include "src/heap-profiler.h"
|
||||
#include "src/heap-snapshot-generator-inl.h"
|
||||
@ -222,54 +221,6 @@ bool RunExtraCode(Isolate* isolate, char* utf8_source) {
|
||||
}
|
||||
|
||||
|
||||
void CheckDefaultReservationSizes(const i::StartupSerializer& startup_ser,
|
||||
const i::PartialSerializer& context_ser) {
|
||||
#ifdef DEBUG
|
||||
i::List<i::SerializedData::Reservation> startup_reservations;
|
||||
i::List<i::SerializedData::Reservation> context_reservations;
|
||||
startup_ser.EncodeReservations(&startup_reservations);
|
||||
context_ser.EncodeReservations(&context_reservations);
|
||||
for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
|
||||
// Exactly one chunk per space.
|
||||
CHECK(startup_reservations[space].is_last());
|
||||
CHECK(startup_reservations[space].is_last());
|
||||
uint32_t sum = startup_reservations[space].chunk_size() +
|
||||
context_reservations[space].chunk_size();
|
||||
uint32_t limit = 0;
|
||||
const int constant_pool_delta = i::FLAG_enable_ool_constant_pool ? 48 : 0;
|
||||
switch (space) {
|
||||
case i::NEW_SPACE:
|
||||
limit = 3 * i::kPointerSize;
|
||||
break;
|
||||
case i::OLD_POINTER_SPACE:
|
||||
limit = (128 + constant_pool_delta) * i::kPointerSize * i::KB;
|
||||
break;
|
||||
case i::OLD_DATA_SPACE:
|
||||
limit = 192 * i::KB;
|
||||
break;
|
||||
case i::MAP_SPACE:
|
||||
limit = 16 * i::kPointerSize * i::KB;
|
||||
break;
|
||||
case i::CELL_SPACE:
|
||||
limit = 16 * i::kPointerSize * i::KB;
|
||||
break;
|
||||
case i::PROPERTY_CELL_SPACE:
|
||||
limit = 8 * i::kPointerSize * i::KB;
|
||||
break;
|
||||
case i::CODE_SPACE:
|
||||
limit = RoundUp((480 - constant_pool_delta) * i::KB *
|
||||
i::FullCodeGenerator::kBootCodeSizeMultiplier / 100,
|
||||
i::kPointerSize);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
CHECK_LE(sum, limit);
|
||||
}
|
||||
#endif // DEBUG
|
||||
}
|
||||
|
||||
|
||||
StartupData V8::CreateSnapshotDataBlob(char* custom_source) {
|
||||
Isolate::CreateParams params;
|
||||
params.enable_serializer = true;
|
||||
@ -313,13 +264,7 @@ StartupData V8::CreateSnapshotDataBlob(char* custom_source) {
|
||||
context_ser.Serialize(&raw_context);
|
||||
ser.SerializeWeakReferences();
|
||||
|
||||
i::SnapshotData sd(snapshot_sink, ser);
|
||||
i::SnapshotData csd(context_sink, context_ser);
|
||||
|
||||
if (custom_source == NULL) CheckDefaultReservationSizes(ser, context_ser);
|
||||
|
||||
result = i::Snapshot::CreateSnapshotBlob(sd.RawData(), csd.RawData(),
|
||||
metadata);
|
||||
result = i::Snapshot::CreateSnapshotBlob(ser, context_ser, metadata);
|
||||
}
|
||||
}
|
||||
isolate->Dispose();
|
||||
|
@ -102,29 +102,21 @@ class FullCodeGenerator: public AstVisitor {
|
||||
// Platform-specific code size multiplier.
|
||||
#if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87
|
||||
static const int kCodeSizeMultiplier = 105;
|
||||
static const int kBootCodeSizeMultiplier = 100;
|
||||
#elif V8_TARGET_ARCH_X64
|
||||
static const int kCodeSizeMultiplier = 170;
|
||||
static const int kBootCodeSizeMultiplier = 140;
|
||||
#elif V8_TARGET_ARCH_ARM
|
||||
static const int kCodeSizeMultiplier = 149;
|
||||
static const int kBootCodeSizeMultiplier = 110;
|
||||
#elif V8_TARGET_ARCH_ARM64
|
||||
// TODO(all): Copied ARM value. Check this is sensible for ARM64.
|
||||
static const int kCodeSizeMultiplier = 149;
|
||||
static const int kBootCodeSizeMultiplier = 110;
|
||||
#elif V8_TARGET_ARCH_PPC64
|
||||
static const int kCodeSizeMultiplier = 200;
|
||||
static const int kBootCodeSizeMultiplier = 120;
|
||||
#elif V8_TARGET_ARCH_PPC
|
||||
static const int kCodeSizeMultiplier = 200;
|
||||
static const int kBootCodeSizeMultiplier = 120;
|
||||
#elif V8_TARGET_ARCH_MIPS
|
||||
static const int kCodeSizeMultiplier = 149;
|
||||
static const int kBootCodeSizeMultiplier = 120;
|
||||
#elif V8_TARGET_ARCH_MIPS64
|
||||
static const int kCodeSizeMultiplier = 149;
|
||||
static const int kBootCodeSizeMultiplier = 170;
|
||||
#else
|
||||
#error Unsupported target architecture.
|
||||
#endif
|
||||
|
@ -1038,6 +1038,10 @@ bool PagedSpace::Expand() {
|
||||
|
||||
intptr_t size = AreaSize();
|
||||
|
||||
if (anchor_.next_page() == &anchor_) {
|
||||
size = Snapshot::SizeOfFirstPage(identity());
|
||||
}
|
||||
|
||||
Page* p = heap()->isolate()->memory_allocator()->AllocatePage(size, this,
|
||||
executable());
|
||||
if (p == NULL) return false;
|
||||
|
@ -2462,12 +2462,11 @@ void SerializedData::AllocateData(int size) {
|
||||
}
|
||||
|
||||
|
||||
SnapshotData::SnapshotData(const SnapshotByteSink& sink,
|
||||
const Serializer& ser) {
|
||||
SnapshotData::SnapshotData(const Serializer& ser) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
List<Reservation> reservations;
|
||||
ser.EncodeReservations(&reservations);
|
||||
const List<byte>& payload = sink.data();
|
||||
const List<byte>& payload = ser.sink()->data();
|
||||
|
||||
// Calculate sizes.
|
||||
int reservation_size = reservations.length() * kInt32Size;
|
||||
|
@ -734,6 +734,8 @@ class Serializer : public SerializerDeserializer {
|
||||
return max_chunk_size_[space];
|
||||
}
|
||||
|
||||
SnapshotByteSink* sink() const { return sink_; }
|
||||
|
||||
Isolate* isolate_;
|
||||
|
||||
SnapshotByteSink* sink_;
|
||||
@ -742,8 +744,9 @@ class Serializer : public SerializerDeserializer {
|
||||
BackReferenceMap back_reference_map_;
|
||||
RootIndexMap root_index_map_;
|
||||
|
||||
friend class ObjectSerializer;
|
||||
friend class Deserializer;
|
||||
friend class ObjectSerializer;
|
||||
friend class SnapshotData;
|
||||
|
||||
private:
|
||||
CodeAddressMap* code_address_map_;
|
||||
@ -897,7 +900,7 @@ class CodeSerializer : public Serializer {
|
||||
class SnapshotData : public SerializedData {
|
||||
public:
|
||||
// Used when producing.
|
||||
SnapshotData(const SnapshotByteSink& sink, const Serializer& ser);
|
||||
explicit SnapshotData(const Serializer& ser);
|
||||
|
||||
// Used when consuming.
|
||||
explicit SnapshotData(const Vector<const byte> snapshot)
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "src/api.h"
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/serialize.h"
|
||||
#include "src/full-codegen.h"
|
||||
#include "src/snapshot.h"
|
||||
|
||||
namespace v8 {
|
||||
@ -34,6 +34,18 @@ bool Snapshot::EmbedsScript() {
|
||||
}
|
||||
|
||||
|
||||
uint32_t Snapshot::SizeOfFirstPage(AllocationSpace space) {
|
||||
DCHECK(space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE);
|
||||
if (!HaveASnapshotToStartFrom()) {
|
||||
return static_cast<uint32_t>(MemoryAllocator::PageAreaSize(space));
|
||||
}
|
||||
uint32_t size;
|
||||
int offset = kFirstPageSizesOffset + (space - FIRST_PAGED_SPACE) * kInt32Size;
|
||||
memcpy(&size, SnapshotBlob().data + offset, kInt32Size);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
bool Snapshot::Initialize(Isolate* isolate) {
|
||||
if (!HaveASnapshotToStartFrom()) return false;
|
||||
base::ElapsedTimer timer;
|
||||
@ -82,9 +94,70 @@ MaybeHandle<Context> Snapshot::NewContextFromSnapshot(
|
||||
}
|
||||
|
||||
|
||||
void CalculateFirstPageSizes(bool is_default_snapshot,
|
||||
const SnapshotData& startup_snapshot,
|
||||
const SnapshotData& context_snapshot,
|
||||
uint32_t* sizes_out) {
|
||||
Vector<const SerializedData::Reservation> startup_reservations =
|
||||
startup_snapshot.Reservations();
|
||||
Vector<const SerializedData::Reservation> context_reservations =
|
||||
context_snapshot.Reservations();
|
||||
int startup_index = 0;
|
||||
int context_index = 0;
|
||||
for (int space = 0; space < i::Serializer::kNumberOfSpaces; space++) {
|
||||
bool single_chunk = true;
|
||||
while (!startup_reservations[startup_index].is_last()) {
|
||||
single_chunk = false;
|
||||
startup_index++;
|
||||
}
|
||||
while (!context_reservations[context_index].is_last()) {
|
||||
single_chunk = false;
|
||||
context_index++;
|
||||
}
|
||||
|
||||
uint32_t required = kMaxUInt32;
|
||||
if (single_chunk) {
|
||||
// If both the startup snapshot data and the context snapshot data on
|
||||
// this space fit in a single page, then we consider limiting the size
|
||||
// of the first page. For this, we add the chunk sizes and some extra
|
||||
// allowance. This way we achieve a smaller startup memory footprint.
|
||||
required = (startup_reservations[startup_index].chunk_size() +
|
||||
2 * context_reservations[context_index].chunk_size()) +
|
||||
Page::kObjectStartOffset;
|
||||
} else {
|
||||
// We expect the vanilla snapshot to only require on page per space.
|
||||
DCHECK(!is_default_snapshot);
|
||||
}
|
||||
|
||||
if (space >= FIRST_PAGED_SPACE && space <= LAST_PAGED_SPACE) {
|
||||
uint32_t max_size =
|
||||
MemoryAllocator::PageAreaSize(static_cast<AllocationSpace>(space));
|
||||
sizes_out[space - FIRST_PAGED_SPACE] = Min(required, max_size);
|
||||
} else {
|
||||
DCHECK(single_chunk);
|
||||
}
|
||||
startup_index++;
|
||||
context_index++;
|
||||
}
|
||||
|
||||
DCHECK_EQ(startup_reservations.length(), startup_index);
|
||||
DCHECK_EQ(context_reservations.length(), context_index);
|
||||
}
|
||||
|
||||
|
||||
v8::StartupData Snapshot::CreateSnapshotBlob(
|
||||
const Vector<const byte> startup_data,
|
||||
const Vector<const byte> context_data, Snapshot::Metadata metadata) {
|
||||
const i::StartupSerializer& startup_ser,
|
||||
const i::PartialSerializer& context_ser, Snapshot::Metadata metadata) {
|
||||
SnapshotData startup_snapshot(startup_ser);
|
||||
SnapshotData context_snapshot(context_ser);
|
||||
Vector<const byte> startup_data = startup_snapshot.RawData();
|
||||
Vector<const byte> context_data = context_snapshot.RawData();
|
||||
|
||||
uint32_t first_page_sizes[kNumPagedSpaces];
|
||||
|
||||
CalculateFirstPageSizes(metadata.embeds_script(), startup_snapshot,
|
||||
context_snapshot, first_page_sizes);
|
||||
|
||||
int startup_length = startup_data.length();
|
||||
int context_length = context_data.length();
|
||||
int context_offset = ContextOffset(startup_length);
|
||||
@ -93,6 +166,8 @@ v8::StartupData Snapshot::CreateSnapshotBlob(
|
||||
char* data = new char[length];
|
||||
|
||||
memcpy(data + kMetadataOffset, &metadata.RawValue(), kInt32Size);
|
||||
memcpy(data + kFirstPageSizesOffset, first_page_sizes,
|
||||
kNumPagedSpaces * kInt32Size);
|
||||
memcpy(data + kStartupLengthOffset, &startup_length, kInt32Size);
|
||||
memcpy(data + kStartupDataOffset, startup_data.begin(), startup_length);
|
||||
memcpy(data + context_offset, context_data.begin(), context_length);
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "src/isolate.h"
|
||||
#include "src/serialize.h"
|
||||
|
||||
#ifndef V8_SNAPSHOT_H_
|
||||
#define V8_SNAPSHOT_H_
|
||||
@ -39,12 +40,14 @@ class Snapshot : public AllStatic {
|
||||
|
||||
static bool EmbedsScript();
|
||||
|
||||
static uint32_t SizeOfFirstPage(AllocationSpace space);
|
||||
|
||||
// To be implemented by the snapshot source.
|
||||
static const v8::StartupData SnapshotBlob();
|
||||
|
||||
static v8::StartupData CreateSnapshotBlob(
|
||||
const Vector<const byte> startup_data,
|
||||
const Vector<const byte> context_data, Metadata metadata);
|
||||
const StartupSerializer& startup_ser,
|
||||
const PartialSerializer& context_ser, Snapshot::Metadata metadata);
|
||||
|
||||
#ifdef DEBUG
|
||||
static bool SnapshotIsValid(v8::StartupData* snapshot_blob);
|
||||
@ -55,8 +58,19 @@ class Snapshot : public AllStatic {
|
||||
static Vector<const byte> ExtractContextData(const v8::StartupData* data);
|
||||
static Metadata ExtractMetadata(const v8::StartupData* data);
|
||||
|
||||
// Snapshot blob layout:
|
||||
// [0] metadata
|
||||
// [1 - 6] pre-calculated first page sizes for paged spaces
|
||||
// [7] serialized start up data length
|
||||
// ... serialized start up data
|
||||
// ... serialized context data
|
||||
|
||||
static const int kNumPagedSpaces = LAST_PAGED_SPACE - FIRST_PAGED_SPACE + 1;
|
||||
|
||||
static const int kMetadataOffset = 0;
|
||||
static const int kStartupLengthOffset = kMetadataOffset + kInt32Size;
|
||||
static const int kFirstPageSizesOffset = kMetadataOffset + kInt32Size;
|
||||
static const int kStartupLengthOffset =
|
||||
kFirstPageSizesOffset + kNumPagedSpaces * kInt32Size;
|
||||
static const int kStartupDataOffset = kStartupLengthOffset + kInt32Size;
|
||||
|
||||
static int ContextOffset(int startup_length) {
|
||||
|
@ -5093,3 +5093,25 @@ TEST(PathTracer) {
|
||||
CcTest::i_isolate()->heap()->TracePathToObject(*o);
|
||||
}
|
||||
#endif // DEBUG
|
||||
|
||||
|
||||
TEST(FirstPageFitsStartup) {
|
||||
// Test that the first page sizes provided by the default snapshot are large
|
||||
// enough to fit everything right after startup and creating one context.
|
||||
// If this test fails, we are allocating too much aside from deserialization.
|
||||
if (!Snapshot::HaveASnapshotToStartFrom()) return;
|
||||
if (Snapshot::EmbedsScript()) return;
|
||||
CcTest::InitializeVM();
|
||||
LocalContext env;
|
||||
PagedSpaces spaces(CcTest::heap());
|
||||
for (PagedSpace* s = spaces.next(); s != NULL; s = spaces.next()) {
|
||||
uint32_t default_size = s->AreaSize();
|
||||
uint32_t reduced_size = Snapshot::SizeOfFirstPage(s->identity());
|
||||
if (reduced_size == default_size) continue;
|
||||
int counter = 0;
|
||||
Page* page = NULL;
|
||||
for (PageIterator it(s); it.has_next(); page = it.next()) counter++;
|
||||
CHECK_LE(counter, 1);
|
||||
CHECK(static_cast<uint32_t>(page->area_size()) == reduced_size);
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ static bool WriteToFile(Isolate* isolate, const char* snapshot_file) {
|
||||
SnapshotByteSink sink;
|
||||
StartupSerializer ser(isolate, &sink);
|
||||
ser.Serialize();
|
||||
SnapshotData snapshot_data(sink, ser);
|
||||
SnapshotData snapshot_data(ser);
|
||||
WritePayload(snapshot_data.RawData(), snapshot_file);
|
||||
return true;
|
||||
}
|
||||
@ -376,8 +376,8 @@ UNINITIALIZED_TEST(PartialSerialization) {
|
||||
|
||||
startup_serializer.SerializeWeakReferences();
|
||||
|
||||
SnapshotData startup_snapshot(startup_sink, startup_serializer);
|
||||
SnapshotData partial_snapshot(partial_sink, partial_serializer);
|
||||
SnapshotData startup_snapshot(startup_serializer);
|
||||
SnapshotData partial_snapshot(partial_serializer);
|
||||
|
||||
WritePayload(partial_snapshot.RawData(), FLAG_testing_serialization_file);
|
||||
WritePayload(startup_snapshot.RawData(), startup_name.start());
|
||||
@ -494,8 +494,8 @@ UNINITIALIZED_TEST(ContextSerialization) {
|
||||
partial_serializer.Serialize(&raw_context);
|
||||
startup_serializer.SerializeWeakReferences();
|
||||
|
||||
SnapshotData startup_snapshot(startup_sink, startup_serializer);
|
||||
SnapshotData partial_snapshot(partial_sink, partial_serializer);
|
||||
SnapshotData startup_snapshot(startup_serializer);
|
||||
SnapshotData partial_snapshot(partial_serializer);
|
||||
|
||||
WritePayload(partial_snapshot.RawData(), FLAG_testing_serialization_file);
|
||||
WritePayload(startup_snapshot.RawData(), startup_name.start());
|
||||
@ -630,8 +630,8 @@ UNINITIALIZED_TEST(CustomContextSerialization) {
|
||||
partial_serializer.Serialize(&raw_context);
|
||||
startup_serializer.SerializeWeakReferences();
|
||||
|
||||
SnapshotData startup_snapshot(startup_sink, startup_serializer);
|
||||
SnapshotData partial_snapshot(partial_sink, partial_serializer);
|
||||
SnapshotData startup_snapshot(startup_serializer);
|
||||
SnapshotData partial_snapshot(partial_serializer);
|
||||
|
||||
WritePayload(partial_snapshot.RawData(), FLAG_testing_serialization_file);
|
||||
WritePayload(startup_snapshot.RawData(), startup_name.start());
|
||||
|
@ -1209,7 +1209,7 @@ UNINITIALIZED_TEST(OneByteArrayJoin) {
|
||||
v8::Isolate::CreateParams create_params;
|
||||
// Set heap limits.
|
||||
create_params.constraints.set_max_semi_space_size(1);
|
||||
create_params.constraints.set_max_old_space_size(4);
|
||||
create_params.constraints.set_max_old_space_size(5);
|
||||
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
||||
isolate->Enter();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user