Files
sdk/runtime/lib/growable_array.cc
T
Aske Simon Christensen cec963f028 The current growth strategy for growable arrays allocates a backing array of size 2 at (empty) creation and doubles the size whenever the capacity is insufficient while adding elements.
I collected statistics for the sizes and capacities of growable arrays which are promoted to old-space or survive an old-space gc when running dart2js and Fasta. For these applications, the vast majority of arrays stay empty. More than half of the total object size of promoted backing arrays is backing for empty growable arrays.

Furthermore, since the overhead for an array is 3 words (header, type parameters and length), and object sizes are rounded up to an even number of words, we waste one word for all even-sized arrays.

This CL changes the growth strategy so that empty growable arrays are created with a shared, zero-sized array as backing, avoiding the allocation of a backing array if no elements are added. When the array needs to grow, it starts out at 3 and grows to double size plus one each time: 7, 15, 31, ...

A few places in the VM code need to handle these shared, zero-sized arrays specially. In particular, the Array::MakeArray function needs to allocate a new, empty array if its result is to be returned to Dart code.

Benchmarks suggest that the change improves memory usage by a few percent overall and does not significantly affect run time.

BUG=
R=erikcorry@google.com

Review-Url: https://codereview.chromium.org/2949803002 .
2017-06-22 10:51:54 +02:00

105 lines
3.5 KiB
C++

// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include "platform/assert.h"
#include "vm/assembler.h"
#include "vm/bootstrap_natives.h"
#include "vm/exceptions.h"
#include "vm/native_entry.h"
#include "vm/object.h"
namespace dart {
DEFINE_NATIVE_ENTRY(GrowableList_allocate, 2) {
const TypeArguments& type_arguments =
TypeArguments::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
if (data.Length() < 0) {
Exceptions::ThrowRangeError("length",
Integer::Handle(Integer::New(data.Length())),
0, // This is the limit the user sees.
Array::kMaxElements);
}
const GrowableObjectArray& new_array =
GrowableObjectArray::Handle(GrowableObjectArray::New(data));
new_array.SetTypeArguments(type_arguments);
return new_array.raw();
}
DEFINE_NATIVE_ENTRY(GrowableList_getIndexed, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
if ((index.Value() < 0) || (index.Value() >= array.Length())) {
Exceptions::ThrowRangeError("index", index, 0, array.Length() - 1);
}
const Instance& obj = Instance::CheckedHandle(array.At(index.Value()));
return obj.raw();
}
DEFINE_NATIVE_ENTRY(GrowableList_setIndexed, 3) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, index, arguments->NativeArgAt(1));
if ((index.Value() < 0) || (index.Value() >= array.Length())) {
Exceptions::ThrowRangeError("index", index, 0, array.Length() - 1);
}
GET_NON_NULL_NATIVE_ARGUMENT(Instance, value, arguments->NativeArgAt(2));
array.SetAt(index.Value(), value);
return Object::null();
}
DEFINE_NATIVE_ENTRY(GrowableList_getLength, 1) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
return Smi::New(array.Length());
}
DEFINE_NATIVE_ENTRY(GrowableList_getCapacity, 1) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
return Smi::New(array.Capacity());
}
DEFINE_NATIVE_ENTRY(GrowableList_setLength, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Smi, length, arguments->NativeArgAt(1));
ASSERT((length.Value() >= 0) && (length.Value() <= array.Capacity()));
array.SetLength(length.Value());
return Object::null();
}
DEFINE_NATIVE_ENTRY(GrowableList_setData, 2) {
const GrowableObjectArray& array =
GrowableObjectArray::CheckedHandle(arguments->NativeArgAt(0));
GET_NON_NULL_NATIVE_ARGUMENT(Array, data, arguments->NativeArgAt(1));
ASSERT(data.Length() >= 0);
array.SetData(data);
return Object::null();
}
DEFINE_NATIVE_ENTRY(Internal_makeListFixedLength, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(GrowableObjectArray, array,
arguments->NativeArgAt(0));
return Array::MakeFixedLength(array, /* unique = */ true);
}
DEFINE_NATIVE_ENTRY(Internal_makeFixedListUnmodifiable, 1) {
GET_NON_NULL_NATIVE_ARGUMENT(Array, array, arguments->NativeArgAt(0));
array.MakeImmutable();
return array.raw();
}
} // namespace dart