Bug 1853720 - Add a "hot" variant of AUTO_PROFILER_LABEL, for lower overhead when the profiler is disabled. r=aabh,profiler-reviewers

The overhead is showing up in Speedometer 3, especially in the innerHTML setter
when it calls into the frame constructor.

Full breakdown of callers across sp3 is here: https://share.firefox.dev/3rfckTG

Time spent in AutoProfilerLabel during TodoMVC-jQuery innerHTML:
Before: https://share.firefox.dev/3Znlydp 378 sampes
After: https://share.firefox.dev/45VdVgr 71 samples

Differential Revision: https://phabricator.services.mozilla.com/D188487
This commit is contained in:
Markus Stange 2023-09-25 19:36:41 +00:00
parent abfc9f5199
commit b474c8da56
5 changed files with 96 additions and 31 deletions

View File

@ -811,7 +811,7 @@ nsresult EventDispatcher::Dispatch(EventTarget* aTarget,
nsEventStatus* aEventStatus,
EventDispatchingCallback* aCallback,
nsTArray<EventTarget*>* aTargets) {
AUTO_PROFILER_LABEL("EventDispatcher::Dispatch", OTHER);
AUTO_PROFILER_LABEL_HOT("EventDispatcher::Dispatch", OTHER);
NS_ASSERTION(aEvent, "Trying to dispatch without WidgetEvent!");
NS_ENSURE_TRUE(!aEvent->mFlags.mIsBeingDispatched,

View File

@ -2607,8 +2607,8 @@ nsIFrame* nsCSSFrameConstructor::ConstructDocElementFrame(
}
nsIFrame* nsCSSFrameConstructor::ConstructRootFrame() {
AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ConstructRootFrame",
LAYOUT_FrameConstruction);
AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ConstructRootFrame",
LAYOUT_FrameConstruction);
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
ServoStyleSet* styleSet = mPresShell->StyleSet();
@ -6491,8 +6491,8 @@ void nsCSSFrameConstructor::ContentAppended(nsIContent* aFirstNewContent,
MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
!RestyleManager()->IsInStyleRefresh());
AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentAppended",
LAYOUT_FrameConstruction);
AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentAppended",
LAYOUT_FrameConstruction);
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
#ifdef DEBUG
@ -6803,8 +6803,8 @@ void nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aStartChild,
MOZ_ASSERT(aInsertionKind == InsertionKind::Sync ||
!RestyleManager()->IsInStyleRefresh());
AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRangeInserted",
LAYOUT_FrameConstruction);
AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentRangeInserted",
LAYOUT_FrameConstruction);
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
MOZ_ASSERT(aStartChild, "must always pass a child");
@ -7301,8 +7301,8 @@ bool nsCSSFrameConstructor::ContentRemoved(nsIContent* aChild,
MOZ_ASSERT(aChild);
MOZ_ASSERT(!aChild->IsRootOfNativeAnonymousSubtree() || !aOldNextSibling,
"Anonymous roots don't have siblings");
AUTO_PROFILER_LABEL("nsCSSFrameConstructor::ContentRemoved",
LAYOUT_FrameConstruction);
AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::ContentRemoved",
LAYOUT_FrameConstruction);
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
nsPresContext* presContext = mPresShell->GetPresContext();
MOZ_ASSERT(presContext, "Our presShell should have a valid presContext");
@ -7679,8 +7679,8 @@ bool nsCSSFrameConstructor::EnsureFrameForTextNodeIsCreatedAfterFlush(
void nsCSSFrameConstructor::CharacterDataChanged(
nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
AUTO_PROFILER_LABEL("nsCSSFrameConstructor::CharacterDataChanged",
LAYOUT_FrameConstruction);
AUTO_PROFILER_LABEL_HOT("nsCSSFrameConstructor::CharacterDataChanged",
LAYOUT_FrameConstruction);
AUTO_LAYOUT_PHASE_ENTRY_POINT(mPresShell->GetPresContext(), FrameC);
if ((aContent->HasFlag(NS_CREATE_FRAME_IF_NON_WHITESPACE) &&

View File

@ -94,7 +94,7 @@ static bool ContainingBlockMayHaveChanged(const ComputedStyle& aOldStyle,
nsChangeHint ComputedStyle::CalcStyleDifference(const ComputedStyle& aNewStyle,
uint32_t* aEqualStructs) const {
AUTO_PROFILER_LABEL("ComputedStyle::CalcStyleDifference", LAYOUT);
AUTO_PROFILER_LABEL_HOT("ComputedStyle::CalcStyleDifference", LAYOUT);
static_assert(StyleStructConstants::kStyleStructCount <= 32,
"aEqualStructs is not big enough");

View File

@ -6869,7 +6869,7 @@ bool profiler_capture_backtrace_into(ProfileChunkedBuffer& aChunkedBuffer,
UniquePtr<ProfileChunkedBuffer> profiler_capture_backtrace() {
MOZ_RELEASE_ASSERT(CorePS::Exists());
AUTO_PROFILER_LABEL("profiler_capture_backtrace", PROFILER);
AUTO_PROFILER_LABEL_HOT("profiler_capture_backtrace", PROFILER);
// Quick is-active and feature check before allocating a buffer.
// If NoMarkerStacks is set, we don't want to capture a backtrace.

View File

@ -11,6 +11,7 @@
#ifndef ProfilerLabels_h
#define ProfilerLabels_h
#include "mozilla/ProfilerState.h"
#include "mozilla/ProfilerThreadState.h"
#include "js/ProfilingCategory.h"
@ -38,11 +39,25 @@ struct JSContext;
// like this: "ClassName::FunctionName:PartName".
//
// Use AUTO_PROFILER_LABEL_DYNAMIC_* if you want to add additional / dynamic
// information to the label stack frame.
// information to the label stack frame, and AUTO_PROFILER_LABEL_HOT if you're
// instrumenting functions for which overhead on the order of nanoseconds is
// noticeable.
#define AUTO_PROFILER_LABEL(label, categoryPair) \
mozilla::AutoProfilerLabel PROFILER_RAII( \
label, nullptr, JS::ProfilingCategoryPair::categoryPair)
// Like AUTO_PROFILER_LABEL, but for super-hot code where overhead must be
// kept to the absolute minimum. This variant doesn't push the label if the
// profiler isn't running.
// Don't use this for long-running functions: If the profiler is started in
// the middle of the function, this label won't be on the stack until the
// function is entered the next time. As a result, category information for
// samples at the start of the profile can be misleading.
// For short-running functions, that's often an acceptable trade-off.
#define AUTO_PROFILER_LABEL_HOT(label, categoryPair) \
mozilla::AutoProfilerLabelHot PROFILER_RAII( \
label, nullptr, JS::ProfilingCategoryPair::categoryPair)
// Similar to AUTO_PROFILER_LABEL, but that adds the RELEVANT_FOR_JS flag.
#define AUTO_PROFILER_LABEL_RELEVANT_FOR_JS(label, categoryPair) \
mozilla::AutoProfilerLabel PROFILER_RAII( \
@ -171,7 +186,7 @@ struct JSContext;
// ProfilingStack from the JS context, and avoids almost all overhead in the
// case where the profiler is disabled.
#define AUTO_PROFILER_LABEL_FAST(label, categoryPair, ctx) \
mozilla::AutoProfilerLabel PROFILER_RAII( \
mozilla::AutoProfilerLabelHot PROFILER_RAII( \
ctx, label, nullptr, JS::ProfilingCategoryPair::categoryPair)
// Similar to AUTO_PROFILER_LABEL_FAST, but also takes an extra string and an
@ -179,7 +194,7 @@ struct JSContext;
// js::ProfilingStackFrame::Flags enum.
#define AUTO_PROFILER_LABEL_DYNAMIC_FAST(label, dynamicString, categoryPair, \
ctx, flags) \
mozilla::AutoProfilerLabel PROFILER_RAII( \
mozilla::AutoProfilerLabelHot PROFILER_RAII( \
ctx, label, dynamicString, JS::ProfilingCategoryPair::categoryPair, \
flags)
@ -194,14 +209,25 @@ class MOZ_RAII AutoProfilerLabel {
JS::ProfilingCategoryPair aCategoryPair,
uint32_t aFlags = 0) {}
// This is the AUTO_PROFILER_LABEL_FAST variant.
AutoProfilerLabel(JSContext* aJSContext, const char* aLabel,
const char* aDynamicString,
JS::ProfilingCategoryPair aCategoryPair, uint32_t aFlags) {}
~AutoProfilerLabel() {}
};
class MOZ_RAII AutoProfilerLabelHot {
public:
// This is the AUTO_PROFILER_LABEL_HOT variant.
AutoProfilerLabelHot(const char* aLabel, const char* aDynamicString,
JS::ProfilingCategoryPair aCategoryPair,
uint32_t aFlags = 0) {}
// This is the AUTO_PROFILER_LABEL_FAST variant.
AutoProfilerLabelHot(JSContext* aJSContext, const char* aLabel,
const char* aDynamicString,
JS::ProfilingCategoryPair aCategoryPair,
uint32_t aFlags) {}
~AutoProfilerLabelHot() {}
};
#else // !MOZ_GECKO_PROFILER
// This class creates a non-owning ProfilingStack reference. Objects of this
@ -227,23 +253,62 @@ class MOZ_RAII AutoProfilerLabel {
}
}
// This is the AUTO_PROFILER_LABEL_FAST variant. It retrieves the
// ProfilingStack from the JSContext and does nothing if the profiler is
// inactive.
AutoProfilerLabel(JSContext* aJSContext, const char* aLabel,
const char* aDynamicString,
JS::ProfilingCategoryPair aCategoryPair, uint32_t aFlags) {
mProfilingStack = js::GetContextProfilingStackIfEnabled(aJSContext);
~AutoProfilerLabel() {
// This function runs both on and off the main thread.
if (mProfilingStack) {
mProfilingStack->pop();
}
}
private:
// We save a ProfilingStack pointer in the ctor so we don't have to redo the
// TLS lookup in the dtor.
ProfilingStack* mProfilingStack;
};
class MOZ_RAII AutoProfilerLabelHot {
public:
// This is the AUTO_PROFILER_LABEL_HOT variant. It does nothing if
// the profiler is inactive.
AutoProfilerLabelHot(const char* aLabel, const char* aDynamicString,
JS::ProfilingCategoryPair aCategoryPair,
uint32_t aFlags = 0) {
if (MOZ_LIKELY(!profiler_is_active())) {
mProfilingStack = nullptr;
return;
}
// Get the ProfilingStack from TLS.
mProfilingStack = profiler::ThreadRegistration::WithOnThreadRefOr(
[](profiler::ThreadRegistration::OnThreadRef aThread) {
return &aThread.UnlockedConstReaderAndAtomicRWRef()
.ProfilingStackRef();
},
nullptr);
if (mProfilingStack) {
mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
aCategoryPair, aFlags);
}
}
~AutoProfilerLabel() {
// This function runs both on and off the main thread.
// This is the AUTO_PROFILER_LABEL_FAST variant. It retrieves the
// ProfilingStack from the JSContext and does nothing if the profiler is
// inactive.
AutoProfilerLabelHot(JSContext* aJSContext, const char* aLabel,
const char* aDynamicString,
JS::ProfilingCategoryPair aCategoryPair,
uint32_t aFlags) {
mProfilingStack = js::GetContextProfilingStackIfEnabled(aJSContext);
if (MOZ_UNLIKELY(mProfilingStack)) {
mProfilingStack->pushLabelFrame(aLabel, aDynamicString, this,
aCategoryPair, aFlags);
}
}
if (mProfilingStack) {
~AutoProfilerLabelHot() {
// This function runs both on and off the main thread.
if (MOZ_UNLIKELY(mProfilingStack)) {
mProfilingStack->pop();
}
}