Skip to content

Commit

Permalink
Bug 1387956 - Overhaul ComputedValues measurement, and add style stru…
Browse files Browse the repository at this point in the history
…cts measurement. r=bholley.

This patch moves measurement of ComputedValues objects from Rust to C++.
Measurement now happens (a) via DOM elements and (b) remaining elements via
the frame tree. Likewise for the style structs hanging off ComputedValues
objects.

Here is an example of the output.

> ├──27,600,448 B (26.49%) -- active/window(https://en.wikipedia.org/wiki/Barack_Obama)
> │  ├──12,772,544 B (12.26%) -- layout
> │  │  ├───4,483,744 B (04.30%) -- frames
> │  │  │   ├──1,653,552 B (01.59%) ── nsInlineFrame
> │  │  │   ├──1,415,760 B (01.36%) ── nsTextFrame
> │  │  │   ├────431,376 B (00.41%) ── nsBlockFrame
> │  │  │   ├────340,560 B (00.33%) ── nsHTMLScrollFrame
> │  │  │   ├────302,544 B (00.29%) ── nsContinuingTextFrame
> │  │  │   ├────156,408 B (00.15%) ── nsBulletFrame
> │  │  │   ├─────73,024 B (00.07%) ── nsPlaceholderFrame
> │  │  │   ├─────27,656 B (00.03%) ── sundries
> │  │  │   ├─────23,520 B (00.02%) ── nsTableCellFrame
> │  │  │   ├─────16,704 B (00.02%) ── nsImageFrame
> │  │  │   ├─────15,488 B (00.01%) ── nsTableRowFrame
> │  │  │   ├─────13,776 B (00.01%) ── nsTableColFrame
> │  │  │   └─────13,376 B (00.01%) ── nsTableFrame
> │  │  ├───3,412,192 B (03.28%) -- servo-style-structs
> │  │  │   ├──1,288,224 B (01.24%) ── Display
> │  │  │   ├────742,400 B (00.71%) ── Position
> │  │  │   ├────308,736 B (00.30%) ── Font
> │  │  │   ├────226,512 B (00.22%) ── Background
> │  │  │   ├────218,304 B (00.21%) ── TextReset
> │  │  │   ├────214,896 B (00.21%) ── Text
> │  │  │   ├────130,560 B (00.13%) ── Border
> │  │  │   ├─────81,408 B (00.08%) ── UIReset
> │  │  │   ├─────61,440 B (00.06%) ── Padding
> │  │  │   ├─────38,176 B (00.04%) ── UserInterface
> │  │  │   ├─────29,232 B (00.03%) ── Margin
> │  │  │   ├─────21,824 B (00.02%) ── sundries
> │  │  │   ├─────20,080 B (00.02%) ── Color
> │  │  │   ├─────20,080 B (00.02%) ── Column
> │  │  │   └─────10,320 B (00.01%) ── Effects
> │  │  ├───2,227,680 B (02.14%) -- computed-values
> │  │  │   ├──1,182,928 B (01.14%) ── non-dom
> │  │  │   └──1,044,752 B (01.00%) ── dom
> │  │  ├───1,500,016 B (01.44%) ── text-runs
> │  │  ├─────492,640 B (00.47%) ── line-boxes
> │  │  ├─────326,688 B (00.31%) ── frame-properties
> │  │  ├─────301,760 B (00.29%) ── pres-shell
> │  │  ├──────27,648 B (00.03%) ── pres-contexts
> │  │  └─────────176 B (00.00%) ── style-sets

The 'servo-style-structs' and 'computed-values' sub-trees are new. (Prior to
this patch, ComputedValues under DOM elements were tallied under the the
'dom/element-nodes' sub-tree, and ComputedValues not under DOM element were
ignored.) 'servo-style-structs/sundries' aggregates all the style structs that
are smaller than 8 KiB.

Other notable things done by the patch are as follows.

- It significantly changes the signatures of the methods measuring nsINode and
  its subclasses, in order to handle the tallying of style structs separately
  from element-nodes. Likewise for nsIFrame.

- It renames the 'layout/style-structs' sub-tree as
  'layout/gecko-style-structs', to clearly distinguish it from the new
  'layout/servo-style-structs' sub-tree.

- It adds some FFI functions to access various Rust-side data structures from
  C++ code.

- There is a nasty hack used twice to measure Arcs, by stepping backwards from
  an interior pointer to a base pointer. It works, but I want to replace it
  with something better eventually. The "XXX WARNING" comments have details.

- It makes DMD print a line to the console if it sees a pointer it doesn't
  recognise. This is useful for detecting when we are measuring an interior
  pointer instead of a base pointer, which is bad but easy to do when Arcs are
  involved.

- It removes the Rust code for measuring CVs, because it's now all done on the
  C++ side.

MozReview-Commit-ID: BKebACLKtCi

--HG--
extra : rebase_source : 4d9a8c6b198a0ff025b811759a6bfa9f33a260ba
  • Loading branch information
nnethercote committed Aug 11, 2017
1 parent cb12286 commit 57c26c9
Show file tree
Hide file tree
Showing 30 changed files with 443 additions and 157 deletions.
43 changes: 32 additions & 11 deletions dom/base/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4143,19 +4143,40 @@ Element::SetCustomElementData(CustomElementData* aData)

MOZ_DEFINE_MALLOC_SIZE_OF(ServoElementMallocSizeOf)

size_t
Element::SizeOfExcludingThis(SizeOfState& aState) const
{
size_t n = FragmentOrElement::SizeOfExcludingThis(aState);
void
Element::AddSizeOfExcludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
FragmentOrElement::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);

if (HasServoData()) {
// Measure mServoData, excluding the ComputedValues. This measurement
// counts towards the element's size. We use ServoElementMallocSizeOf
// rather thang |aState.mMallocSizeOf| to better distinguish in DMD's
// output the memory measured within Servo code.
*aNodeSize +=
Servo_Element_SizeOfExcludingThisAndCVs(ServoElementMallocSizeOf,
&aState.mSeenPtrs, this);

// Now measure just the ComputedValues (and style structs) under
// mServoData. This counts towards the relevant fields in |aSizes|.
RefPtr<ServoStyleContext> sc;
if (Servo_Element_HasPrimaryComputedValues(this)) {
sc = Servo_Element_GetPrimaryComputedValues(this).Consume();
if (!aState.HaveSeenPtr(sc.get())) {
sc->AddSizeOfIncludingThis(aState, aSizes, /* isDOM = */ true);
}

// Measure mServoData. We use ServoElementMallocSizeOf rather than
// |aState.mMallocSizeOf| to distinguish in DMD's output the memory
// measured within Servo code.
if (mServoData.Get()) {
n += Servo_Element_SizeOfExcludingThis(ServoElementMallocSizeOf,
&aState.mSeenPtrs, this);
for (size_t i = 0; i < nsCSSPseudoElements::kEagerPseudoCount; i++) {
if (Servo_Element_HasPseudoComputedValues(this, i)) {
sc = Servo_Element_GetPseudoComputedValues(this, i).Consume();
if (!aState.HaveSeenPtr(sc.get())) {
sc->AddSizeOfIncludingThis(aState, aSizes, /* isDOM = */ true);
}
}
}
}
}
return n;
}

struct DirtyDescendantsBit {
Expand Down
2 changes: 1 addition & 1 deletion dom/base/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ class Element : public FragmentOrElement

NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID)

NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS

NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;

Expand Down
15 changes: 7 additions & 8 deletions dom/base/FragmentOrElement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2502,19 +2502,18 @@ FragmentOrElement::FireNodeRemovedForChildren()
}
}

size_t
FragmentOrElement::SizeOfExcludingThis(SizeOfState& aState) const
void
FragmentOrElement::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = 0;
n += nsIContent::SizeOfExcludingThis(aState);
n += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);
nsIContent::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += mAttrsAndChildren.SizeOfExcludingThis(aState.mMallocSizeOf);

nsDOMSlots* slots = GetExistingDOMSlots();
if (slots) {
n += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
*aNodeSize += slots->SizeOfIncludingThis(aState.mMallocSizeOf);
}

return n;
}

void
Expand Down
2 changes: 1 addition & 1 deletion dom/base/FragmentOrElement.h
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class FragmentOrElement : public nsIContent

NS_DECL_CYCLE_COLLECTING_ISUPPORTS

NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS

// nsINode interface methods
virtual uint32_t GetChildCount() const override;
Expand Down
57 changes: 33 additions & 24 deletions dom/base/nsDocument.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12390,26 +12390,25 @@ nsDocument::GetVisibilityState(nsAString& aState)
}

/* virtual */ void
nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
nsIDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aSizes) const
{
aWindowSizes.mDOMOtherSize +=
nsINode::SizeOfExcludingThis(aWindowSizes.mState);
nsINode::AddSizeOfExcludingThis(aSizes.mState, aSizes.mStyleSizes,
&aSizes.mDOMOtherSize);

if (mPresShell) {
mPresShell->AddSizeOfIncludingThis(aWindowSizes);
mPresShell->AddSizeOfIncludingThis(aSizes);
}

aWindowSizes.mPropertyTablesSize +=
mPropertyTable.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
aSizes.mPropertyTablesSize +=
mPropertyTable.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
for (uint32_t i = 0, count = mExtraPropertyTables.Length();
i < count; ++i) {
aWindowSizes.mPropertyTablesSize +=
mExtraPropertyTables[i]->SizeOfIncludingThis(
aWindowSizes.mState.mMallocSizeOf);
aSizes.mPropertyTablesSize +=
mExtraPropertyTables[i]->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
}

if (EventListenerManager* elm = GetExistingListenerManager()) {
aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
aSizes.mDOMEventListenersCount += elm->ListenerCount();
}

// Measurement of the following members may be added later if DMD finds it
Expand Down Expand Up @@ -12440,10 +12439,12 @@ SizeOfOwnedSheetArrayExcludingThis(const nsTArray<RefPtr<StyleSheet>>& aSheets,
return n;
}

size_t
nsDocument::SizeOfExcludingThis(SizeOfState& aState) const
void
nsDocument::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
// This SizeOfExcludingThis() overrides the one from nsINode. But
// This AddSizeOfExcludingThis() overrides the one from nsINode. But
// nsDocuments can only appear at the top of the DOM tree, and we use the
// specialized DocAddSizeOfExcludingThis() in that case. So this should never
// be called.
Expand All @@ -12453,40 +12454,48 @@ nsDocument::SizeOfExcludingThis(SizeOfState& aState) const
void
nsDocument::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const
{
nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);

for (nsIContent* node = nsINode::GetFirstChild();
node;
node = node->GetNextNode(this))
{
size_t nodeSize = node->SizeOfIncludingThis(aWindowSizes.mState);
size_t* p;
size_t nodeSize = 0;
node->AddSizeOfIncludingThis(aWindowSizes.mState, aWindowSizes.mStyleSizes,
&nodeSize);

// This is where we transfer the nodeSize obtained from
// nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
switch (node->NodeType()) {
case nsIDOMNode::ELEMENT_NODE:
p = &aWindowSizes.mDOMElementNodesSize;
aWindowSizes.mDOMElementNodesSize += nodeSize;
break;
case nsIDOMNode::TEXT_NODE:
p = &aWindowSizes.mDOMTextNodesSize;
aWindowSizes.mDOMTextNodesSize += nodeSize;
break;
case nsIDOMNode::CDATA_SECTION_NODE:
p = &aWindowSizes.mDOMCDATANodesSize;
aWindowSizes.mDOMCDATANodesSize += nodeSize;
break;
case nsIDOMNode::COMMENT_NODE:
p = &aWindowSizes.mDOMCommentNodesSize;
aWindowSizes.mDOMCommentNodesSize += nodeSize;
break;
default:
p = &aWindowSizes.mDOMOtherSize;
aWindowSizes.mDOMOtherSize += nodeSize;
break;
}

*p += nodeSize;

if (EventListenerManager* elm = node->GetExistingListenerManager()) {
aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
}
}

// IMPORTANT: for our ComputedValues measurements, we want to measure
// ComputedValues accessible from DOM elements before ComputedValues not
// accessible from DOM elements (i.e. accessible only from the frame tree).
//
// Therefore, the measurement of the nsIDocument superclass must happen after
// the measurement of DOM nodes (above), because nsIDocument contains the
// PresShell, which contains the frame tree.
nsIDocument::DocAddSizeOfExcludingThis(aWindowSizes);

aWindowSizes.mStyleSheetsSize +=
SizeOfOwnedSheetArrayExcludingThis(mStyleSheets,
aWindowSizes.mState.mMallocSizeOf);
Expand Down
2 changes: 1 addition & 1 deletion dom/base/nsDocument.h
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ class nsDocument : public nsIDocument,

NS_DECL_CYCLE_COLLECTING_ISUPPORTS

NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS

virtual void Reset(nsIChannel *aChannel, nsILoadGroup *aLoadGroup) override;
virtual void ResetToURI(nsIURI *aURI, nsILoadGroup *aLoadGroup,
Expand Down
11 changes: 6 additions & 5 deletions dom/base/nsGenericDOMDataNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,11 +1109,12 @@ nsGenericDOMDataNode::GetAttributeChangeHint(const nsIAtom* aAttribute,
return nsChangeHint(0);
}

size_t
nsGenericDOMDataNode::SizeOfExcludingThis(SizeOfState& aState) const
void
nsGenericDOMDataNode::AddSizeOfExcludingThis(SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = nsIContent::SizeOfExcludingThis(aState);
n += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
return n;
nsIContent::AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
*aNodeSize += mText.SizeOfExcludingThis(aState.mMallocSizeOf);
}

2 changes: 1 addition & 1 deletion dom/base/nsGenericDOMDataNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class nsGenericDOMDataNode : public nsIContent
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS

NS_DECL_SIZEOF_EXCLUDING_THIS
NS_DECL_ADDSIZEOFEXCLUDINGTHIS

explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
explicit nsGenericDOMDataNode(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
Expand Down
9 changes: 4 additions & 5 deletions dom/base/nsINode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2569,13 +2569,13 @@ nsINode::GetAccessibleNode()
return nullptr;
}

size_t
nsINode::SizeOfExcludingThis(SizeOfState& aState) const
void
nsINode::AddSizeOfExcludingThis(SizeOfState& aState, nsStyleSizes& aSizes,
size_t* aNodeSize) const
{
size_t n = 0;
EventListenerManager* elm = GetExistingListenerManager();
if (elm) {
n += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
*aNodeSize += elm->SizeOfIncludingThis(aState.mMallocSizeOf);
}

// Measurement of the following members may be added later if DMD finds it is
Expand All @@ -2586,7 +2586,6 @@ nsINode::SizeOfExcludingThis(SizeOfState& aState) const
// The following members are not measured:
// - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
// non-owning
return n;
}

#define EVENT(name_, id_, type_, struct_) \
Expand Down
31 changes: 20 additions & 11 deletions dom/base/nsINode.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
#include "nsNodeInfoManager.h" // for use in NodePrincipal()
#include "nsPropertyTable.h" // for typedefs
#include "nsTObserverArray.h" // for member
#include "nsWindowSizes.h" // for nsStyleSizes
#include "mozilla/ErrorResult.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/SizeOfState.h" // for SizeOfState
#include "mozilla/dom/EventTarget.h" // for base class
#include "js/TypeDecls.h" // for Handle, Value, JSObject, JSContext
#include "mozilla/dom/DOMString.h"
Expand Down Expand Up @@ -264,13 +264,13 @@ class nsMutationGuard {
};

// This should be used for any nsINode sub-class that has fields of its own
// that it needs to measure; any sub-class that doesn't use it will inherit
// SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be
// defined, it is inherited from nsINode.
// This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
#define NS_DECL_SIZEOF_EXCLUDING_THIS \
virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) \
const override;
// that it needs to measure; any sub-class that doesn't use it will inherit
// AddSizeOfExcludingThis from its super-class. AddSizeOfIncludingThis() need
// not be defined, it is inherited from nsINode.
#define NS_DECL_ADDSIZEOFEXCLUDINGTHIS \
virtual void AddSizeOfExcludingThis(mozilla::SizeOfState& aState, \
nsStyleSizes& aSizes, \
size_t* aNodeSize) const override;

// Categories of node properties
// 0 is global.
Expand Down Expand Up @@ -305,6 +305,10 @@ class nsINode : public mozilla::dom::EventTarget

NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)

// The |aNodeSize| outparam on this function is where the actual node size
// value is put. It gets added to the appropriate value within |aSizes| by
// nsDocument::DocAddSizeOfExcludingThis().
//
// Among the sub-classes that inherit (directly or indirectly) from nsINode,
// measurement of the following members may be added later if DMD finds it is
// worthwhile:
Expand All @@ -328,15 +332,20 @@ class nsINode : public mozilla::dom::EventTarget
// The following members don't need to be measured:
// - nsIContent: mPrimaryFrame, because it's non-owning and measured elsewhere
//
virtual size_t SizeOfExcludingThis(mozilla::SizeOfState& aState) const;
virtual void AddSizeOfExcludingThis(mozilla::SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const;

// SizeOfIncludingThis doesn't need to be overridden by sub-classes because
// sub-classes of nsINode are guaranteed to be laid out in memory in such a
// way that |this| points to the start of the allocated object, even in
// methods of nsINode's sub-classes, so aState.mMallocSizeOf(this) is always
// safe to call no matter which object it was invoked on.
virtual size_t SizeOfIncludingThis(mozilla::SizeOfState& aState) const {
return aState.mMallocSizeOf(this) + SizeOfExcludingThis(aState);
virtual void AddSizeOfIncludingThis(mozilla::SizeOfState& aState,
nsStyleSizes& aSizes,
size_t* aNodeSize) const {
*aNodeSize += aState.mMallocSizeOf(this);
AddSizeOfExcludingThis(aState, aSizes, aNodeSize);
}

friend class nsNodeUtils;
Expand Down
Loading

0 comments on commit 57c26c9

Please sign in to comment.