Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: track memory retainer fields #26161

Merged
merged 1 commit into from
Feb 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/memory_tracker-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,13 @@ void MemoryTracker::Track(const MemoryRetainer* retainer,
PopNode();
}

void MemoryTracker::TrackInlineField(const MemoryRetainer* retainer,
const char* edge_name) {
Track(retainer, edge_name);
CHECK(CurrentNode());
CurrentNode()->size_ -= retainer->SelfSize();
}
addaleax marked this conversation as resolved.
Show resolved Hide resolved

MemoryRetainerNode* MemoryTracker::CurrentNode() const {
if (node_stack_.empty()) return nullptr;
return node_stack_.top();
Expand Down
11 changes: 11 additions & 0 deletions src/memory_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,17 @@ class MemoryTracker {
inline void Track(const MemoryRetainer* retainer,
const char* edge_name = nullptr);

// Useful for parents that do not wish to perform manual
// adjustments to its `SelfSize()` when embedding retainer
// objects inline.
// Put a memory container into the graph, create an edge from
// the current node if there is one on the stack - there should
// be one, of the container object which the current field is part of.
// Reduce the size of memory from the container so as to avoid
// duplication in accounting.
inline void TrackInlineField(const MemoryRetainer* retainer,
Copy link
Member

@joyeecheung joyeecheung Feb 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what this is trying to achieve, is there a use case of this API?

Normally the size of the pointer to the retainer should be accounted into the self size of the parent but self size of the retainer should not be accounted, so I can't tell when it's necessary to do a subtraction - maybe there is a misuse of the API?

Copy link
Member

@joyeecheung joyeecheung Feb 22, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is trying to address the issues that came up from #26099 there are two ways to solve that:

  1. Do not use SET_SELF_SIZE(Worker), which is merely a convenience macro that implements Worker::SelfSize() with sizeof(Worker) to calculate the self size of the worker - that's where we encounter the first tracking of the inline field. Instead, override Worker::SelfSize() to exclude the size of the non-pointer fields, and track them later in Worker::MemoryInfo().
  2. Keep using SET_SELF_SIZE(Worker) for the initial calculation, but rename this method into something like SplitNonPointerField() (SplitStackAllocatedField()?), because this does not necessarily add more memory into the tracker - instead it's splitting off existing memory that have already been tracked, and potentially adding more (if the retainer overrides MemoryInfo() to add additional fields that are not accounted by a sizeof())

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joyeecheung - thanks.

Probably these many scenarios exists (with a component cmp and a container cnt with cnt a retainer):

  • cmp is a normal primitive field: self size of cnt covers size of cmp
  • cmp is a composite object (with or without primitive fields): self size of cnt covers size of cmp
  • cmp is a pointer to a composite object: self size of cnt covers size of cmp (size of a pointer)
  • cmp is a composite object (with pointer type fields): self size of cnt covers size of cmp, not the dynamic memory [ example: messageport in worker ]
  • cmp is a composite retainer object (with pointer types): [ current use case ]
  • cmp is a pointer to a retainer object (with pointer types): [ none at the moment? ]

We have Worker with 200 bytes excluding 2 inline AsyncRequest fields of 72 bytes each. So the question is: should we add those to the worker (because those belongs to worker) or add separately - as they are retainers themselves.

If I follow your approach 1, we need to perform that in all such scenarios? in which case arguably the parent retainer (Worker's referencing object if any) could do the same for worker, and potentially render the Retainer abstraction meaningless?

second proposal looks fair to me, but would love to hear from @addaleax as well. Can SplitStackAllocatedField be somewhat misleading, as there is no memory that is stack-allocated here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because this does not necessarily add more memory into the tracker - instead it's splitting off existing memory that have already been tracked, and potentially adding more (if the retainer overrides MemoryInfo() to add additional fields that are not accounted by a sizeof())

I’m not sure, but I don’t really see this as a issue/as not “tracking” these fields? Then again, re-naming is not a big deal, so I’m okay with anything.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add those to the worker (because those belongs to worker) or add separately - as they are retainers themselves.

That depends on whether we want to display AsyncRequest separately as a Node referenced by the Worker node, and any other fields referenced by those AsyncRequests recursively.

If I follow your approach 1, we need to perform that in all such scenarios? in which case arguably the parent retainer (Worker's referencing object if any) could do the same for worker, and potentially render the Retainer abstraction meaningless?

The abstraction helps adding nodes recursively instead of tracking everything from the root, allowing the MemoryRetainer implementations define how they want their fields to be tracked.

Can SplitStackAllocatedField be somewhat misleading, as there is no memory that is stack-allocated here?

Right, the fields are not really stack allocated if the whole thing is not stack allocated.

I’m not sure, but I don’t really see this as a issue/as not “tracking” these fields?

I am fine with the current naming if the comments are changed to reflect when this method should be used and what issue it is trying to address. Use when a retainer is embedded in another. and Reduce the size of memory from the container so that retentions are accounted properly. is too ambiguous IMO, the core of the issue this method is trying to solve arise from that the parent does not implement its SelfSize() correctly to exclude the memory that is supposed to be tracked as the self size of another node.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joyeecheung - thanks, I will amend the comments, aligning with above conversation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joyeecheung - amended the comment, ptal!

const char* edge_name = nullptr);

inline v8::EmbedderGraph* graph() { return graph_; }
inline v8::Isolate* isolate() { return isolate_; }

Expand Down