Commit d3104923 authored by Jeremy Roman's avatar Jeremy Roman Committed by Commit Bot

Implement and use VectorSegment to avoid repeated allocation of ZoneVector properties.

The parser holds a single vector whose backing storage is reused in calls
to ParseJsonObject, so that once we reach the peak number of unstored
properties no more allocations are required.

This improves performance of parsing inputs like those in Speedometer VanillaJS
by about 2% in my local measurement, and would presumably do better on more
pathological inputs.

This should also have the side effect of reducing peak memory usage at this time
slightly, since we do fewer zone allocations which cannot be freed until the
parse finishes.

Bug: chromium:771227
Change-Id: I8aa1514b37a74f82539f95f94292c8fa1582d66a
Reviewed-on: https://chromium-review.googlesource.com/789511Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49693}
parent b03b1bd9
......@@ -20,6 +20,38 @@
namespace v8 {
namespace internal {
namespace {
// A vector-like data structure that uses a larger vector for allocation, and
// provides limited utility access. The original vector must not be used for the
// duration, and it may even be reallocated. This allows vector storage to be
// reused for the properties of sibling objects.
template <typename Container>
class VectorSegment {
public:
using value_type = typename Container::value_type;
explicit VectorSegment(Container* container)
: container_(*container), begin_(container->size()) {}
~VectorSegment() { container_.resize(begin_); }
Vector<const value_type> GetVector() const {
return Vector<const value_type>(&container_[begin_],
container_.size() - begin_);
}
template <typename T>
void push_back(T&& value) {
container_.push_back(std::forward<T>(value));
}
private:
Container& container_;
const typename Container::size_type begin_;
};
} // namespace
MaybeHandle<Object> JsonParseInternalizer::Internalize(Isolate* isolate,
Handle<Object> object,
Handle<Object> reviver) {
......@@ -111,7 +143,8 @@ JsonParser<seq_one_byte>::JsonParser(Isolate* isolate, Handle<String> source)
zone_(isolate_->allocator(), ZONE_NAME),
object_constructor_(isolate_->native_context()->object_function(),
isolate_),
position_(-1) {
position_(-1),
properties_(&zone_) {
source_ = String::Flatten(source_);
pretenure_ = (source_length_ >= kPretenureTreshold) ? TENURED : NOT_TENURED;
......@@ -333,7 +366,7 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
factory()->NewJSObject(object_constructor(), pretenure_);
Handle<Map> map(json_object->map());
int descriptor = 0;
ZoneVector<Handle<Object>> properties(zone());
VectorSegment<ZoneVector<Handle<Object>>> properties(&properties_);
DCHECK_EQ(c0_, '{');
bool transitioning = true;
......@@ -424,7 +457,7 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
DCHECK(!transitioning);
// Commit the intermediate state to the object and stop transitioning.
CommitStateToJsonObject(json_object, map, &properties);
CommitStateToJsonObject(json_object, map, properties.GetVector());
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, value)
.Check();
......@@ -432,7 +465,7 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
// If we transitioned until the very end, transition the map now.
if (transitioning) {
CommitStateToJsonObject(json_object, map, &properties);
CommitStateToJsonObject(json_object, map, properties.GetVector());
} else {
while (MatchSkipWhiteSpace(',')) {
HandleScope local_scope(isolate());
......@@ -480,15 +513,14 @@ Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
template <bool seq_one_byte>
void JsonParser<seq_one_byte>::CommitStateToJsonObject(
Handle<JSObject> json_object, Handle<Map> map,
ZoneVector<Handle<Object>>* properties) {
Vector<const Handle<Object>> properties) {
JSObject::AllocateStorageForMap(json_object, map);
DCHECK(!json_object->map()->is_dictionary_map());
DisallowHeapAllocation no_gc;
DescriptorArray* descriptors = json_object->map()->instance_descriptors();
int length = static_cast<int>(properties->size());
for (int i = 0; i < length; i++) {
Handle<Object> value = (*properties)[i];
for (int i = 0; i < properties.length(); i++) {
Handle<Object> value = properties[i];
// Initializing store.
json_object->WriteToField(i, descriptors->GetDetails(i), *value);
}
......
......@@ -144,7 +144,7 @@ class JsonParser BASE_EMBEDDED {
Zone* zone() { return &zone_; }
void CommitStateToJsonObject(Handle<JSObject> json_object, Handle<Map> map,
ZoneVector<Handle<Object>>* properties);
Vector<const Handle<Object>> properties);
Handle<String> source_;
int source_length_;
......@@ -157,6 +157,9 @@ class JsonParser BASE_EMBEDDED {
Handle<JSFunction> object_constructor_;
uc32 c0_;
int position_;
// Property handles are stored here inside ParseJsonObject.
ZoneVector<Handle<Object>> properties_;
};
} // namespace internal
......
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