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

Reland: 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.

Reland switches to use std::vector::data instead of operator[] to avoid an index
check in debug MSVC. In such cases the out-of-bounds pointer cannot be
dereferenced, so it is legal.

Bug: chromium:771227
Change-Id: I21837196372c904bfc799cd14353a73d11dcff32
Reviewed-on: https://chromium-review.googlesource.com/804062Reviewed-by: 's avatarMarja Hölttä <marja@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49997}
parent 5c8ac0f3
......@@ -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_.data() + 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) {
......@@ -110,7 +142,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;
......@@ -332,7 +365,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;
......@@ -423,7 +456,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();
......@@ -431,7 +464,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());
......@@ -479,15 +512,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);
}
......
......@@ -145,7 +145,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