Bug 1017418 (part 2) - Avoid more slop in nsTArray. r=froydnj.

This commit is contained in:
Nicholas Nethercote 2014-06-01 16:08:50 -07:00
parent 7c04dbe0fe
commit 1f6501ee8c
3 changed files with 86 additions and 65 deletions

View File

@ -99,6 +99,44 @@ bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const {
bool
IsTwiceTheRequiredBytesRepresentableAsUint32(size_t capacity, size_t elemSize);
template<class Alloc, class Copy>
void
nsTArray_base<Alloc, Copy>::GoodSizeForCapacity(size_t capacity,
size_t elemSize,
size_t& nbytes,
size_t& newCapacity)
{
// We increase our capacity so that |capacity * elemSize + sizeof(Header)| is
// a size that minimizes slop -- if |nbytes| is less than |pageSize| we round
// up to the next power of two, otherwise we round up to the next multiple of
// |pageSize|.
const size_t pageSizeBytes = 12;
const size_t pageSize = 1 << pageSizeBytes;
nbytes = capacity * elemSize + sizeof(Header);
if (nbytes >= pageSize) {
// Round up to the next multiple of pageSize.
nbytes = pageSize * ((nbytes + pageSize - 1) / pageSize);
} else {
// Round up to the next power of two. See
// http://graphics.stanford.edu/~seander/bithacks.html
nbytes = nbytes - 1;
nbytes |= nbytes >> 1;
nbytes |= nbytes >> 2;
nbytes |= nbytes >> 4;
nbytes |= nbytes >> 8;
nbytes |= nbytes >> 16;
nbytes++;
MOZ_ASSERT((nbytes & (nbytes - 1)) == 0,
"nsTArray's allocation size should be a power of two!");
}
// How many elements can we fit in |nbytes|?
newCapacity = (nbytes - sizeof(Header)) / elemSize;
MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!");
}
template<class Alloc, class Copy>
typename Alloc::ResultTypeProxy
nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) {
@ -116,68 +154,38 @@ nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSiz
return Alloc::FailureResult();
}
if (mHdr == EmptyHdr()) {
// Malloc() new data
Header *header = static_cast<Header*>
(Alloc::Malloc(sizeof(Header) + capacity * elemSize));
if (!header)
return Alloc::FailureResult();
header->mLength = 0;
header->mCapacity = capacity;
header->mIsAutoArray = 0;
mHdr = header;
return Alloc::SuccessResult();
}
// We increase our capacity so |capacity * elemSize + sizeof(Header)| is the
// next power of two, if this value is less than pageSize bytes, or otherwise
// so it's the next multiple of pageSize.
const size_t pageSizeBytes = 12;
const size_t pageSize = 1 << pageSizeBytes;
size_t minBytes = capacity * elemSize + sizeof(Header);
size_t bytesToAlloc;
if (minBytes >= pageSize) {
// Round up to the next multiple of pageSize.
bytesToAlloc = pageSize * ((minBytes + pageSize - 1) / pageSize);
}
else {
// Round up to the next power of two. See
// http://graphics.stanford.edu/~seander/bithacks.html
bytesToAlloc = minBytes - 1;
bytesToAlloc |= bytesToAlloc >> 1;
bytesToAlloc |= bytesToAlloc >> 2;
bytesToAlloc |= bytesToAlloc >> 4;
bytesToAlloc |= bytesToAlloc >> 8;
bytesToAlloc |= bytesToAlloc >> 16;
bytesToAlloc++;
MOZ_ASSERT((bytesToAlloc & (bytesToAlloc - 1)) == 0,
"nsTArray's allocation size should be a power of two!");
}
size_t nbytes, newCapacity;
GoodSizeForCapacity(capacity, elemSize, nbytes, newCapacity);
Header *header;
if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
// Malloc() and copy
header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc));
if (!header)
if (mHdr == EmptyHdr()) {
// Malloc() new data
header = static_cast<Header*>(Alloc::Malloc(nbytes));
if (!header) {
return Alloc::FailureResult();
}
header->mLength = 0;
header->mIsAutoArray = 0;
} else if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
// Malloc() and copy
header = static_cast<Header*>(Alloc::Malloc(nbytes));
if (!header) {
return Alloc::FailureResult();
}
Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
if (!UsesAutoArrayBuffer())
if (!UsesAutoArrayBuffer()) {
Alloc::Free(mHdr);
}
} else {
// Realloc() existing data
header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc));
if (!header)
header = static_cast<Header*>(Alloc::Realloc(mHdr, nbytes));
if (!header) {
return Alloc::FailureResult();
}
}
// How many elements can we fit in bytesToAlloc?
size_t newCapacity = (bytesToAlloc - sizeof(Header)) / elemSize;
MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!");
header->mCapacity = newCapacity;
mHdr = header;
@ -215,12 +223,17 @@ nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign)
return;
}
size_type size = sizeof(Header) + length * elemSize;
void *ptr = Alloc::Realloc(mHdr, size);
if (!ptr)
return;
mHdr = static_cast<Header*>(ptr);
mHdr->mCapacity = length;
// Only shrink if it would actually result in us using less memory.
size_t nbytes, newCapacity;
GoodSizeForCapacity(length, elemSize, nbytes, newCapacity);
if (newCapacity < Capacity()) {
void *ptr = Alloc::Realloc(mHdr, nbytes);
if (!ptr) {
return;
}
mHdr = static_cast<Header*>(ptr);
mHdr->mCapacity = newCapacity;
}
}
template<class Alloc, class Copy>

View File

@ -391,13 +391,25 @@ protected:
~nsTArray_base();
// For a required capacity, computes how many bytes should be allocated to
// minimize slop, and how many elements will fit in that space.
// @param capacity The required capacity.
// @param elemSize The size of an array element.
// @param capacity The computed size, in bytes (outparam).
// @param capacity The resulting capacity (outparam).
void GoodSizeForCapacity(size_t capacity, size_t elemSize,
size_t& nbytes, size_t& newCapacity);
// Resize the storage if necessary to achieve the requested capacity.
// @param capacity The requested number of array elements.
// @param elemSize The size of an array element.
// @return False if insufficient memory is available; true otherwise.
typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize);
// Resize the storage to the minimum required amount.
// Resize the storage to the minimum required amount. Note that this won't
// reallocate the storage buffer if it wouldn't actually result in us using
// less memory, which is the case when the number of bytes removed is less
// than the slop that would result from the new size.
// @param elemSize The size of an array element.
// @param elemAlign The alignment in bytes of an array element.
void ShrinkCapacity(size_type elemSize, size_t elemAlign);
@ -1368,10 +1380,10 @@ public:
// Allocation
//
// This method may increase the capacity of this array object by the
// specified amount. This method may be called in advance of several
// AppendElement operations to minimize heap re-allocations. This method
// will not reduce the number of elements in this array.
// This method may increase the capacity of this array object to the
// specified amount, or possibly more. This method may be called in advance
// of several AppendElement operations to minimize heap re-allocations. This
// method will not reduce the number of elements in this array.
// @param capacity The desired capacity of this array.
// @return True if the operation succeeded; false if we ran out of memory
typename Alloc::ResultType SetCapacity(size_type capacity) {

View File

@ -115,12 +115,8 @@ static bool test_basic_array(ElementType *data,
}
if (!ary.AppendElements(copy))
return false;
size_t cap = ary.Capacity();
ary.RemoveElementsAt(copy.Length(), copy.Length());
ary.Compact();
if (ary.Capacity() == cap)
return false;
ary.Clear();
if (ary.IndexOf(extra) != ary.NoIndex)
return false;