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:
yangguo 2015-02-20 01:34:00 -08:00 committed by Commit bot
parent c7810004bb
commit afb2706103
10 changed files with 137 additions and 83 deletions

View File

@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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());

View File

@ -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();