Bug 1094564 - Used SegmentedArray in SnowWhiteKiller. r=smaug.

This patch generalizes SegmentedArray a little, and then uses it instead of
nsTArray in SnowWhiteKiller. This avoids some large (sometimes 1 MiB or more)
allocations which were usually mostly unused.
This commit is contained in:
Nicholas Nethercote 2014-11-09 14:57:09 -08:00
parent b5fe55c53e
commit f64f8ba285

View File

@ -2465,16 +2465,18 @@ MayHaveChild(void* aObj, nsCycleCollectionParticipant* aCp)
return cf.MayHaveChild(); return cf.MayHaveChild();
} }
template<class T> template<class T, size_t N>
class SegmentedArrayElement class SegmentedArrayElement
: public LinkedListElement<SegmentedArrayElement<T>> : public LinkedListElement<SegmentedArrayElement<T, N>>
, public AutoFallibleTArray<T, 60> , public AutoFallibleTArray<T, N>
{ {
}; };
template<class T> template<class T, size_t N>
class SegmentedArray class SegmentedArray
{ {
typedef SegmentedArrayElement<T, N> Segment;
public: public:
~SegmentedArray() ~SegmentedArray()
{ {
@ -2483,9 +2485,9 @@ public:
void AppendElement(T& aElement) void AppendElement(T& aElement)
{ {
SegmentedArrayElement<T>* last = mSegments.getLast(); Segment* last = mSegments.getLast();
if (!last || last->Length() == last->Capacity()) { if (!last || last->Length() == last->Capacity()) {
last = new SegmentedArrayElement<T>(); last = new Segment();
mSegments.insertBack(last); mSegments.insertBack(last);
} }
last->AppendElement(aElement); last->AppendElement(aElement);
@ -2493,24 +2495,29 @@ public:
void Clear() void Clear()
{ {
SegmentedArrayElement<T>* first; Segment* first;
while ((first = mSegments.popFirst())) { while ((first = mSegments.popFirst())) {
delete first; delete first;
} }
} }
SegmentedArrayElement<T>* GetFirstSegment() Segment* GetFirstSegment()
{ {
return mSegments.getFirst(); return mSegments.getFirst();
} }
bool IsEmpty() const Segment* GetFirstSegment() const
{
return mSegments.getFirst();
}
bool IsEmpty() const
{ {
return !GetFirstSegment(); return !GetFirstSegment();
} }
private: private:
mozilla::LinkedList<SegmentedArrayElement<T>> mSegments; mozilla::LinkedList<Segment> mSegments;
}; };
// JSPurpleBuffer keeps references to GCThings which might affect the // JSPurpleBuffer keeps references to GCThings which might affect the
@ -2553,8 +2560,8 @@ public:
// pointers which may point into the nursery. The purple buffer never contains // pointers which may point into the nursery. The purple buffer never contains
// pointers to the nursery because nursery gcthings can never be gray and only // pointers to the nursery because nursery gcthings can never be gray and only
// gray things can be inserted into the purple buffer. // gray things can be inserted into the purple buffer.
SegmentedArray<JS::Value> mValues; SegmentedArray<JS::Value, 60> mValues;
SegmentedArray<JSObject*> mObjects; SegmentedArray<JSObject*, 60> mObjects;
}; };
NS_IMPL_CYCLE_COLLECTION_CLASS(JSPurpleBuffer) NS_IMPL_CYCLE_COLLECTION_CLASS(JSPurpleBuffer)
@ -2588,42 +2595,58 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSPurpleBuffer, AddRef) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(JSPurpleBuffer, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(JSPurpleBuffer, Release) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(JSPurpleBuffer, Release)
struct SnowWhiteObject
{
void* mPointer;
nsCycleCollectionParticipant* mParticipant;
nsCycleCollectingAutoRefCnt* mRefCnt;
};
class SnowWhiteKiller : public TraceCallbacks class SnowWhiteKiller : public TraceCallbacks
{ {
struct SnowWhiteObject
{
void* mPointer;
nsCycleCollectionParticipant* mParticipant;
nsCycleCollectingAutoRefCnt* mRefCnt;
};
// Segments are 4 KiB on 32-bit and 8 KiB on 64-bit.
static const size_t kIdealSegmentSize = sizeof(void*) * 1024;
static const size_t kSingleElemSegmentSize =
sizeof(SegmentedArrayElement<SnowWhiteObject, 1>);
static const size_t kSegmentCapacity =
(kIdealSegmentSize - kSingleElemSegmentSize) / sizeof(SnowWhiteObject) + 1;
static const size_t kActualSegmentSize =
sizeof(SegmentedArrayElement<SnowWhiteObject, kSegmentCapacity>);
typedef SegmentedArray<SnowWhiteObject, kSegmentCapacity> ObjectsArray;
public: public:
SnowWhiteKiller(nsCycleCollector* aCollector, uint32_t aMaxCount) SnowWhiteKiller(nsCycleCollector* aCollector, uint32_t aMaxCount)
: mCollector(aCollector) : mCollector(aCollector)
, mObjects()
{ {
MOZ_ASSERT(mCollector, "Calling SnowWhiteKiller after nsCC went away"); MOZ_ASSERT(mCollector, "Calling SnowWhiteKiller after nsCC went away");
while (true) {
if (mObjects.SetCapacity(aMaxCount)) { // The segment capacity should be such that the actual segment size is as
break; // close as possible to the ideal segment size.
} static_assert(
if (aMaxCount == 1) { kIdealSegmentSize - kActualSegmentSize <= sizeof(SnowWhiteObject),
NS_RUNTIMEABORT("Not enough memory to even delete objects!"); "ill-sized SnowWhiteKiller segments"
} );
aMaxCount /= 2;
}
} }
~SnowWhiteKiller() ~SnowWhiteKiller()
{ {
for (uint32_t i = 0; i < mObjects.Length(); ++i) { auto segment = mObjects.GetFirstSegment();
SnowWhiteObject& o = mObjects[i]; while (segment) {
if (!o.mRefCnt->get() && !o.mRefCnt->IsInPurpleBuffer()) { for (uint32_t i = 0; i < segment->Length(); i++) {
mCollector->RemoveObjectFromGraph(o.mPointer); SnowWhiteObject& o = segment->ElementAt(i);
o.mRefCnt->stabilizeForDeletion(); if (!o.mRefCnt->get() && !o.mRefCnt->IsInPurpleBuffer()) {
o.mParticipant->Trace(o.mPointer, *this, nullptr); mCollector->RemoveObjectFromGraph(o.mPointer);
o.mParticipant->DeleteCycleCollectable(o.mPointer); o.mRefCnt->stabilizeForDeletion();
o.mParticipant->Trace(o.mPointer, *this, nullptr);
o.mParticipant->DeleteCycleCollectable(o.mPointer);
}
} }
segment = segment->getNext();
} }
mObjects.Clear();
} }
void void
@ -2635,15 +2658,14 @@ public:
nsCycleCollectionParticipant* cp = aEntry->mParticipant; nsCycleCollectionParticipant* cp = aEntry->mParticipant;
CanonicalizeParticipant(&o, &cp); CanonicalizeParticipant(&o, &cp);
SnowWhiteObject swo = { o, cp, aEntry->mRefCnt }; SnowWhiteObject swo = { o, cp, aEntry->mRefCnt };
if (mObjects.AppendElement(swo)) { mObjects.AppendElement(swo);
aBuffer.Remove(aEntry); aBuffer.Remove(aEntry);
}
} }
} }
bool HasSnowWhiteObjects() const bool HasSnowWhiteObjects() const
{ {
return mObjects.Length() > 0; return !mObjects.IsEmpty();
} }
virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName, virtual void Trace(JS::Heap<JS::Value>* aValue, const char* aName,
@ -2701,7 +2723,7 @@ public:
private: private:
nsCycleCollector* mCollector; nsCycleCollector* mCollector;
FallibleTArray<SnowWhiteObject> mObjects; ObjectsArray mObjects;
}; };
class RemoveSkippableVisitor : public SnowWhiteKiller class RemoveSkippableVisitor : public SnowWhiteKiller