Reintroduce runtime zone to Isolate.
In case tcmalloc is not being used, the malloc()/free() overhead can be significant for several runtime functions like StringReplace. Therefore we reintroduce the runtime_zone into Isolate and reenable the segment caching functionality of Zone. There's now also a simpler version of ZoneScope w/o nesting capabilities. BUG=v8:2759 R=danno@chromium.org, yangguo@chromium.org Review URL: https://codereview.chromium.org/18635003 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15465 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
56ab3e3404
commit
e3f34a58b2
@ -1762,6 +1762,7 @@ Isolate::Isolate()
|
||||
descriptor_lookup_cache_(NULL),
|
||||
handle_scope_implementer_(NULL),
|
||||
unicode_cache_(NULL),
|
||||
runtime_zone_(this),
|
||||
in_use_list_(0),
|
||||
free_list_(0),
|
||||
preallocated_storage_preallocated_(false),
|
||||
@ -1960,6 +1961,9 @@ void Isolate::SetIsolateThreadLocals(Isolate* isolate,
|
||||
Isolate::~Isolate() {
|
||||
TRACE_ISOLATE(destructor);
|
||||
|
||||
// Has to be called while counters_ are still alive
|
||||
runtime_zone_.DeleteKeptSegment();
|
||||
|
||||
// The entry stack must be empty when we get here,
|
||||
// except for the default isolate, where it can
|
||||
// still contain up to one entry stack item
|
||||
|
@ -896,6 +896,7 @@ class Isolate {
|
||||
ASSERT(handle_scope_implementer_);
|
||||
return handle_scope_implementer_;
|
||||
}
|
||||
Zone* runtime_zone() { return &runtime_zone_; }
|
||||
|
||||
UnicodeCache* unicode_cache() {
|
||||
return unicode_cache_;
|
||||
@ -1270,6 +1271,7 @@ class Isolate {
|
||||
v8::ImplementationUtilities::HandleScopeData handle_scope_data_;
|
||||
HandleScopeImplementer* handle_scope_implementer_;
|
||||
UnicodeCache* unicode_cache_;
|
||||
Zone runtime_zone_;
|
||||
PreallocatedStorage in_use_list_;
|
||||
PreallocatedStorage free_list_;
|
||||
bool preallocated_storage_preallocated_;
|
||||
|
@ -3598,8 +3598,8 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalAtomRegExpWithString(
|
||||
ASSERT(subject->IsFlat());
|
||||
ASSERT(replacement->IsFlat());
|
||||
|
||||
Zone zone(isolate);
|
||||
ZoneList<int> indices(8, &zone);
|
||||
ZoneScope zone_scope(isolate->runtime_zone());
|
||||
ZoneList<int> indices(8, zone_scope.zone());
|
||||
ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag());
|
||||
String* pattern =
|
||||
String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex));
|
||||
@ -3608,7 +3608,7 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalAtomRegExpWithString(
|
||||
int replacement_len = replacement->length();
|
||||
|
||||
FindStringIndicesDispatch(
|
||||
isolate, *subject, pattern, &indices, 0xffffffff, &zone);
|
||||
isolate, *subject, pattern, &indices, 0xffffffff, zone_scope.zone());
|
||||
|
||||
int matches = indices.length();
|
||||
if (matches == 0) return *subject;
|
||||
@ -3684,8 +3684,8 @@ MUST_USE_RESULT static MaybeObject* StringReplaceGlobalRegExpWithString(
|
||||
int subject_length = subject->length();
|
||||
|
||||
// CompiledReplacement uses zone allocation.
|
||||
Zone zone(isolate);
|
||||
CompiledReplacement compiled_replacement(&zone);
|
||||
ZoneScope zone_scope(isolate->runtime_zone());
|
||||
CompiledReplacement compiled_replacement(zone_scope.zone());
|
||||
bool simple_replace = compiled_replacement.Compile(replacement,
|
||||
capture_count,
|
||||
subject_length);
|
||||
@ -4218,14 +4218,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringMatch) {
|
||||
|
||||
int capture_count = regexp->CaptureCount();
|
||||
|
||||
Zone zone(isolate);
|
||||
ZoneList<int> offsets(8, &zone);
|
||||
ZoneScope zone_scope(isolate->runtime_zone());
|
||||
ZoneList<int> offsets(8, zone_scope.zone());
|
||||
|
||||
while (true) {
|
||||
int32_t* match = global_cache.FetchNext();
|
||||
if (match == NULL) break;
|
||||
offsets.Add(match[0], &zone); // start
|
||||
offsets.Add(match[1], &zone); // end
|
||||
offsets.Add(match[0], zone_scope.zone()); // start
|
||||
offsets.Add(match[1], zone_scope.zone()); // end
|
||||
}
|
||||
|
||||
if (global_cache.HasException()) return Failure::Exception();
|
||||
@ -6310,18 +6310,18 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) {
|
||||
|
||||
static const int kMaxInitialListCapacity = 16;
|
||||
|
||||
Zone zone(isolate);
|
||||
ZoneScope zone_scope(isolate->runtime_zone());
|
||||
|
||||
// Find (up to limit) indices of separator and end-of-string in subject
|
||||
int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit);
|
||||
ZoneList<int> indices(initial_capacity, &zone);
|
||||
ZoneList<int> indices(initial_capacity, zone_scope.zone());
|
||||
if (!pattern->IsFlat()) FlattenString(pattern);
|
||||
|
||||
FindStringIndicesDispatch(isolate, *subject, *pattern,
|
||||
&indices, limit, &zone);
|
||||
&indices, limit, zone_scope.zone());
|
||||
|
||||
if (static_cast<uint32_t>(indices.length()) < limit) {
|
||||
indices.Add(subject_length, &zone);
|
||||
indices.Add(subject_length, zone_scope.zone());
|
||||
}
|
||||
|
||||
// The list indices now contains the end of each part to create.
|
||||
|
77
src/zone.cc
77
src/zone.cc
@ -78,31 +78,82 @@ Zone::Zone(Isolate* isolate)
|
||||
|
||||
|
||||
Zone::~Zone() {
|
||||
DeleteAll();
|
||||
DeleteKeptSegment();
|
||||
|
||||
ASSERT(segment_bytes_allocated_ == 0);
|
||||
}
|
||||
|
||||
|
||||
void Zone::DeleteAll() {
|
||||
#ifdef DEBUG
|
||||
// Constant byte value used for zapping dead memory in debug mode.
|
||||
static const unsigned char kZapDeadByte = 0xcd;
|
||||
#endif
|
||||
|
||||
// Traverse the chained list of segments, zapping
|
||||
// (in debug mode) and freeing every segment
|
||||
Segment* current = segment_head_;
|
||||
while (current != NULL) {
|
||||
// Find a segment with a suitable size to keep around.
|
||||
Segment* keep = segment_head_;
|
||||
while (keep != NULL && keep->size() > kMaximumKeptSegmentSize) {
|
||||
keep = keep->next();
|
||||
}
|
||||
|
||||
// Traverse the chained list of segments, zapping (in debug mode)
|
||||
// and freeing every segment except the one we wish to keep.
|
||||
for (Segment* current = segment_head_; current != NULL; ) {
|
||||
Segment* next = current->next();
|
||||
int size = current->size();
|
||||
if (current == keep) {
|
||||
// Unlink the segment we wish to keep from the list.
|
||||
current->clear_next();
|
||||
} else {
|
||||
int size = current->size();
|
||||
#ifdef DEBUG
|
||||
// Zap the entire current segment (including the header).
|
||||
memset(current, kZapDeadByte, size);
|
||||
// Zap the entire current segment (including the header).
|
||||
memset(current, kZapDeadByte, size);
|
||||
#endif
|
||||
DeleteSegment(current, size);
|
||||
DeleteSegment(current, size);
|
||||
}
|
||||
current = next;
|
||||
}
|
||||
|
||||
// We must clear the position and limit to force
|
||||
// a new segment to be allocated on demand.
|
||||
position_ = limit_ = 0;
|
||||
// If we have found a segment we want to keep, we must recompute the
|
||||
// variables 'position' and 'limit' to prepare for future allocate
|
||||
// attempts. Otherwise, we must clear the position and limit to
|
||||
// force a new segment to be allocated on demand.
|
||||
if (keep != NULL) {
|
||||
Address start = keep->start();
|
||||
position_ = RoundUp(start, kAlignment);
|
||||
limit_ = keep->end();
|
||||
#ifdef DEBUG
|
||||
// Zap the contents of the kept segment (but not the header).
|
||||
memset(start, kZapDeadByte, keep->capacity());
|
||||
#endif
|
||||
} else {
|
||||
position_ = limit_ = 0;
|
||||
}
|
||||
|
||||
// Update the head segment.
|
||||
segment_head_ = NULL;
|
||||
// Update the head segment to be the kept segment (if any).
|
||||
segment_head_ = keep;
|
||||
}
|
||||
|
||||
|
||||
void Zone::DeleteKeptSegment() {
|
||||
#ifdef DEBUG
|
||||
// Constant byte value used for zapping dead memory in debug mode.
|
||||
static const unsigned char kZapDeadByte = 0xcd;
|
||||
#endif
|
||||
|
||||
ASSERT(segment_head_ == NULL || segment_head_->next() == NULL);
|
||||
if (segment_head_ != NULL) {
|
||||
int size = segment_head_->size();
|
||||
#ifdef DEBUG
|
||||
// Zap the entire kept segment (including the header).
|
||||
memset(segment_head_, kZapDeadByte, size);
|
||||
#endif
|
||||
DeleteSegment(segment_head_, size);
|
||||
segment_head_ = NULL;
|
||||
}
|
||||
|
||||
ASSERT(segment_bytes_allocated_ == 0);
|
||||
}
|
||||
|
||||
|
||||
|
29
src/zone.h
29
src/zone.h
@ -66,6 +66,14 @@ class Zone {
|
||||
template <typename T>
|
||||
inline T* NewArray(int length);
|
||||
|
||||
// Deletes all objects and free all memory allocated in the Zone. Keeps one
|
||||
// small (size <= kMaximumKeptSegmentSize) segment around if it finds one.
|
||||
void DeleteAll();
|
||||
|
||||
// Deletes the last small segment kept around by DeleteAll(). You
|
||||
// may no longer allocate in the Zone after a call to this method.
|
||||
void DeleteKeptSegment();
|
||||
|
||||
// Returns true if more memory has been allocated in zones than
|
||||
// the limit allows.
|
||||
inline bool excess_allocation();
|
||||
@ -90,6 +98,9 @@ class Zone {
|
||||
// Never allocate segments larger than this size in bytes.
|
||||
static const int kMaximumSegmentSize = 1 * MB;
|
||||
|
||||
// Never keep segments larger than this size in bytes around.
|
||||
static const int kMaximumKeptSegmentSize = 64 * KB;
|
||||
|
||||
// Report zone excess when allocation exceeds this limit.
|
||||
static const int kExcessLimit = 256 * MB;
|
||||
|
||||
@ -109,10 +120,10 @@ class Zone {
|
||||
|
||||
// Creates a new segment, sets it size, and pushes it to the front
|
||||
// of the segment chain. Returns the new segment.
|
||||
Segment* NewSegment(int size);
|
||||
INLINE(Segment* NewSegment(int size));
|
||||
|
||||
// Deletes the given segment. Does not touch the segment chain.
|
||||
void DeleteSegment(Segment* segment, int size);
|
||||
INLINE(void DeleteSegment(Segment* segment, int size));
|
||||
|
||||
// The free region in the current (front) segment is represented as
|
||||
// the half-open interval [position, limit). The 'position' variable
|
||||
@ -145,6 +156,20 @@ class ZoneObject {
|
||||
};
|
||||
|
||||
|
||||
// The ZoneScope is used to automatically call DeleteAll() on a
|
||||
// Zone when the ZoneScope is destroyed (i.e. goes out of scope)
|
||||
struct ZoneScope {
|
||||
public:
|
||||
explicit ZoneScope(Zone* zone) : zone_(zone) { }
|
||||
~ZoneScope() { zone_->DeleteAll(); }
|
||||
|
||||
Zone* zone() { return zone_; }
|
||||
|
||||
private:
|
||||
Zone* zone_;
|
||||
};
|
||||
|
||||
|
||||
// The ZoneAllocationPolicy is used to specialize generic data
|
||||
// structures to allocate themselves and their elements in the Zone.
|
||||
struct ZoneAllocationPolicy {
|
||||
|
Loading…
Reference in New Issue
Block a user