Reintroduce runtime zone to Isolate.
authorbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 3 Jul 2013 11:40:30 +0000 (11:40 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Wed, 3 Jul 2013 11:40:30 +0000 (11:40 +0000)
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

src/isolate.cc
src/isolate.h
src/runtime.cc
src/zone.cc
src/zone.h

index fec3dc66ab4260b63b6e8a6023d34961e53536bc..3a6f140506df0ecb430eb8df0ebc56c346870814 100644 (file)
@@ -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
index f7d40dde32d8021e804b05dea67989ad2857cc4b..a0aecd8b275bc96c307d0e75d6700e59bb4f8b6a 100644 (file)
@@ -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_;
index 869c17bf784495cc1c354e9cdce0c0a8e4e3df0e..8a4b32195cfd9b82148a883271a4c6c2bc8d36c7 100644 (file)
@@ -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.
index 82a2efa94ea90e8cf0d30cc611e61dc9bc97523e..2a0a0e284610a03abe195bb42384ca8f35561dce 100644 (file)
@@ -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 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;
+  }
 
-  // Update the head segment.
-  segment_head_ = NULL;
+  ASSERT(segment_bytes_allocated_ == 0);
 }
 
 
index 5545e3cede6dc6b80ed3a2a517b771256a5f6790..a12ed79312333b896277a1ec2067f505d4055b3c 100644 (file)
@@ -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 {