From 40c26995eaa87388ee7f96ae10b777afe664c4f0 Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Fri, 17 Nov 2017 17:30:08 +0100 Subject: [PATCH] Bug 1410096 - Move labeled queues to SchedulerGroup. r=smaug This improves the performance of GetEvent and PutEvent in LabeledEventQueue by removing a hash table mapping groups to queues. --- xpcom/threads/AbstractEventQueue.h | 4 +- xpcom/threads/EventQueue.cpp | 2 +- xpcom/threads/EventQueue.h | 3 +- xpcom/threads/LabeledEventQueue.cpp | 50 +++++++++++++------------ xpcom/threads/LabeledEventQueue.h | 19 ++-------- xpcom/threads/MainThreadQueue.h | 8 ++-- xpcom/threads/PrioritizedEventQueue.cpp | 3 ++ xpcom/threads/SchedulerGroup.h | 22 +++++++++++ 8 files changed, 65 insertions(+), 46 deletions(-) diff --git a/xpcom/threads/AbstractEventQueue.h b/xpcom/threads/AbstractEventQueue.h index 2b34e957b557..ce39de709776 100644 --- a/xpcom/threads/AbstractEventQueue.h +++ b/xpcom/threads/AbstractEventQueue.h @@ -19,7 +19,9 @@ enum class EventPriority High, Input, Normal, - Idle + Idle, + + Count }; // AbstractEventQueue is an abstract base class for all our unsynchronized event diff --git a/xpcom/threads/EventQueue.cpp b/xpcom/threads/EventQueue.cpp index b56b1d9d60ef..ef329bc6620d 100644 --- a/xpcom/threads/EventQueue.cpp +++ b/xpcom/threads/EventQueue.cpp @@ -9,7 +9,7 @@ using namespace mozilla; -EventQueue::EventQueue() +EventQueue::EventQueue(EventPriority aPriority) { } diff --git a/xpcom/threads/EventQueue.h b/xpcom/threads/EventQueue.h index cc5ea020e950..e8a582286640 100644 --- a/xpcom/threads/EventQueue.h +++ b/xpcom/threads/EventQueue.h @@ -18,7 +18,8 @@ namespace mozilla { class EventQueue final : public AbstractEventQueue { public: - EventQueue(); + EventQueue() {} + explicit EventQueue(EventPriority aPriority); void PutEvent(already_AddRefed&& aEvent, EventPriority aPriority, diff --git a/xpcom/threads/LabeledEventQueue.cpp b/xpcom/threads/LabeledEventQueue.cpp index d78228898544..19dd02ade719 100644 --- a/xpcom/threads/LabeledEventQueue.cpp +++ b/xpcom/threads/LabeledEventQueue.cpp @@ -13,11 +13,14 @@ using namespace mozilla::dom; +using EpochQueueEntry = SchedulerGroup::EpochQueueEntry; + LinkedList* LabeledEventQueue::sSchedulerGroups; size_t LabeledEventQueue::sLabeledEventQueueCount; SchedulerGroup* LabeledEventQueue::sCurrentSchedulerGroup; -LabeledEventQueue::LabeledEventQueue() +LabeledEventQueue::LabeledEventQueue(EventPriority aPriority) + : mPriority(aPriority) { // LabeledEventQueue should only be used by one consumer since it uses a // single static sSchedulerGroups field. It's hard to assert this, though, so @@ -77,6 +80,8 @@ LabeledEventQueue::PutEvent(already_AddRefed&& aEvent, EventPriority aPriority, const MutexAutoLock& aProofOfLock) { + MOZ_ASSERT(aPriority == mPriority); + nsCOMPtr event(aEvent); MOZ_ASSERT(event.get()); @@ -100,8 +105,8 @@ LabeledEventQueue::PutEvent(already_AddRefed&& aEvent, mNumEvents++; epoch->mNumEvents++; - RunnableEpochQueue* queue = isLabeled ? mLabeled.LookupOrAdd(group) : &mUnlabeled; - queue->Push(QueueEntry(event.forget(), epoch->mEpochNumber)); + RunnableEpochQueue& queue = isLabeled ? group->GetQueue(aPriority) : mUnlabeled; + queue.Push(EpochQueueEntry(event.forget(), epoch->mEpochNumber)); if (group && group->EnqueueEvent() == SchedulerGroup::NewlyQueued) { // This group didn't have any events before. Add it to the @@ -150,13 +155,13 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority, Epoch epoch = mEpochs.FirstElement(); if (!epoch.IsLabeled()) { - QueueEntry& first = mUnlabeled.FirstElement(); + EpochQueueEntry& first = mUnlabeled.FirstElement(); if (!IsReadyToRun(first.mRunnable, nullptr)) { return nullptr; } PopEpoch(); - QueueEntry entry = mUnlabeled.Pop(); + EpochQueueEntry entry = mUnlabeled.Pop(); MOZ_ASSERT(entry.mEpochNumber == epoch.mEpochNumber); MOZ_ASSERT(entry.mRunnable.get()); return entry.mRunnable.forget(); @@ -197,17 +202,15 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority, do { mAvoidActiveTabCount--; - auto queueEntry = mLabeled.Lookup(group); - if (!queueEntry) { + RunnableEpochQueue& queue = group->GetQueue(mPriority); + + if (queue.IsEmpty()) { // This can happen if |group| is in a different LabeledEventQueue than |this|. group = NextSchedulerGroup(group); continue; } - RunnableEpochQueue* queue = queueEntry.Data(); - MOZ_ASSERT(!queue->IsEmpty()); - - QueueEntry& first = queue->FirstElement(); + EpochQueueEntry& first = queue.FirstElement(); if (first.mEpochNumber == epoch.mEpochNumber && IsReadyToRun(first.mRunnable, group)) { sCurrentSchedulerGroup = NextSchedulerGroup(group); @@ -226,10 +229,7 @@ LabeledEventQueue::GetEvent(EventPriority* aPriority, } group->removeFrom(*sSchedulerGroups); } - QueueEntry entry = queue->Pop(); - if (queue->IsEmpty()) { - queueEntry.Remove(); - } + EpochQueueEntry entry = queue.Pop(); return entry.mRunnable.forget(); } @@ -261,24 +261,26 @@ LabeledEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) Epoch& frontEpoch = mEpochs.FirstElement(); if (!frontEpoch.IsLabeled()) { - QueueEntry& entry = mUnlabeled.FirstElement(); + EpochQueueEntry& entry = mUnlabeled.FirstElement(); return IsReadyToRun(entry.mRunnable, nullptr); } - // Go through the labeled queues and look for one whose head is from the - // current epoch and is allowed to run. + // Go through the scheduler groups and look for one that has events + // for the priority of this labeled queue that is in the current + // epoch and is allowed to run. uintptr_t currentEpoch = frontEpoch.mEpochNumber; - for (auto iter = mLabeled.Iter(); !iter.Done(); iter.Next()) { - SchedulerGroup* key = iter.Key(); - RunnableEpochQueue* queue = iter.Data(); - MOZ_ASSERT(!queue->IsEmpty()); + for (SchedulerGroup* group : *sSchedulerGroups) { + RunnableEpochQueue& queue = group->GetQueue(mPriority); + if (queue.IsEmpty()) { + continue; + } - QueueEntry& entry = queue->FirstElement(); + EpochQueueEntry& entry = queue.FirstElement(); if (entry.mEpochNumber != currentEpoch) { continue; } - if (IsReadyToRun(entry.mRunnable, key)) { + if (IsReadyToRun(entry.mRunnable, group)) { return true; } } diff --git a/xpcom/threads/LabeledEventQueue.h b/xpcom/threads/LabeledEventQueue.h index 4f1f1ef86cdb..9cfaec32bb91 100644 --- a/xpcom/threads/LabeledEventQueue.h +++ b/xpcom/threads/LabeledEventQueue.h @@ -10,6 +10,7 @@ #include #include "mozilla/AbstractEventQueue.h" #include "mozilla/LinkedList.h" +#include "mozilla/SchedulerGroup.h" #include "mozilla/Queue.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" @@ -30,7 +31,7 @@ class SchedulerGroup; class LabeledEventQueue final : public AbstractEventQueue { public: - LabeledEventQueue(); + explicit LabeledEventQueue(EventPriority aPriority); ~LabeledEventQueue(); void PutEvent(already_AddRefed&& aEvent, @@ -83,17 +84,6 @@ private: // decrement the number of events in the current epoch. If this number reaches // zero, we pop from the epoch queue. - struct QueueEntry - { - nsCOMPtr mRunnable; - uintptr_t mEpochNumber; - - QueueEntry(already_AddRefed aRunnable, uintptr_t aEpoch) - : mRunnable(aRunnable) - , mEpochNumber(aEpoch) - {} - }; - struct Epoch { static Epoch First(bool aIsLabeled) @@ -131,8 +121,7 @@ private: void PopEpoch(); static SchedulerGroup* NextSchedulerGroup(SchedulerGroup* aGroup); - using RunnableEpochQueue = Queue; - using LabeledMap = nsClassHashtable, RunnableEpochQueue>; + using RunnableEpochQueue = SchedulerGroup::RunnableEpochQueue; using EpochQueue = Queue; // List of SchedulerGroups that might have events. This is static, so it @@ -144,7 +133,6 @@ private: static size_t sLabeledEventQueueCount; static SchedulerGroup* sCurrentSchedulerGroup; - LabeledMap mLabeled; RunnableEpochQueue mUnlabeled; EpochQueue mEpochs; size_t mNumEvents = 0; @@ -154,6 +142,7 @@ private: // foreground and background SchedulerGroups. For details, see its usage in // LabeledEventQueue.cpp. int64_t mAvoidActiveTabCount = 0; + EventPriority mPriority; }; } // namespace mozilla diff --git a/xpcom/threads/MainThreadQueue.h b/xpcom/threads/MainThreadQueue.h index 6172e1b7c9e6..0a55c82702a4 100644 --- a/xpcom/threads/MainThreadQueue.h +++ b/xpcom/threads/MainThreadQueue.h @@ -23,10 +23,10 @@ CreateMainThread(nsIIdlePeriod* aIdlePeriod, SynchronizedQueueT** aSynchronizedQ using MainThreadQueueT = PrioritizedEventQueue; auto queue = MakeUnique( - MakeUnique(), - MakeUnique(), - MakeUnique(), - MakeUnique(), + MakeUnique(EventPriority::High), + MakeUnique(EventPriority::Input), + MakeUnique(EventPriority::Normal), + MakeUnique(EventPriority::Idle), do_AddRef(aIdlePeriod)); MainThreadQueueT* prioritized = queue.get(); diff --git a/xpcom/threads/PrioritizedEventQueue.cpp b/xpcom/threads/PrioritizedEventQueue.cpp index 1e9bf8a5d8f1..8533ebe5e0c3 100644 --- a/xpcom/threads/PrioritizedEventQueue.cpp +++ b/xpcom/threads/PrioritizedEventQueue.cpp @@ -65,6 +65,9 @@ PrioritizedEventQueue::PutEvent(already_AddRefed&& aEv case EventPriority::Idle: mIdleQueue->PutEvent(event.forget(), priority, aProofOfLock); break; + case EventPriority::Count: + MOZ_CRASH("EventPriority::Count isn't a valid priority"); + break; } } diff --git a/xpcom/threads/SchedulerGroup.h b/xpcom/threads/SchedulerGroup.h index 0b9fef284a7a..c5b6f5f2a40d 100644 --- a/xpcom/threads/SchedulerGroup.h +++ b/xpcom/threads/SchedulerGroup.h @@ -7,8 +7,10 @@ #ifndef mozilla_SchedulerGroup_h #define mozilla_SchedulerGroup_h +#include "mozilla/AbstractEventQueue.h" #include "mozilla/AlreadyAddRefed.h" #include "mozilla/LinkedList.h" +#include "mozilla/Queue.h" #include "mozilla/TaskCategory.h" #include "mozilla/ThreadLocal.h" #include "mozilla/TimeStamp.h" @@ -168,6 +170,25 @@ public: }; static void SetValidatingAccess(ValidationType aType); + struct EpochQueueEntry + { + nsCOMPtr mRunnable; + uintptr_t mEpochNumber; + + EpochQueueEntry(already_AddRefed aRunnable, uintptr_t aEpoch) + : mRunnable(aRunnable) + , mEpochNumber(aEpoch) + { + } + }; + + using RunnableEpochQueue = Queue; + + RunnableEpochQueue& GetQueue(mozilla::EventPriority aPriority) + { + return mEventQueues[size_t(aPriority)]; + } + protected: static nsresult InternalUnlabeledDispatch(TaskCategory aCategory, already_AddRefed&& aRunnable); @@ -203,6 +224,7 @@ protected: nsCOMPtr mEventTargets[size_t(TaskCategory::Count)]; RefPtr mAbstractThreads[size_t(TaskCategory::Count)]; + RunnableEpochQueue mEventQueues[size_t(mozilla::EventPriority::Count)]; }; NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerGroup::Runnable, NS_SCHEDULERGROUPRUNNABLE_IID);