Bug 1161405. Part 1 - improve parallelism of nsThreadPool by taking the number of pending events into account when spawning a new thread. r=nfroyd.
authorJW Wang <jwwang@mozilla.com>
Sat, 09 May 2015 07:32:30 +0800
changeset 243326 eac3aa030c4e6ae109f16b342e3c4440d2cc5651
parent 243325 e09e6eb08f509cd15cf3d7a930025abbb359b46b
child 243327 a4700f2b25fca71e6e35a0e4c1c09897b8ad5ad8
push id28738
push usercbook@mozilla.com
push dateTue, 12 May 2015 14:11:31 +0000
treeherdermozilla-central@bedce1b405a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroyd
bugs1161405
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1161405. Part 1 - improve parallelism of nsThreadPool by taking the number of pending events into account when spawning a new thread. r=nfroyd.
xpcom/threads/nsEventQueue.cpp
xpcom/threads/nsEventQueue.h
xpcom/threads/nsThreadPool.cpp
--- a/xpcom/threads/nsEventQueue.cpp
+++ b/xpcom/threads/nsEventQueue.cpp
@@ -114,8 +114,44 @@ nsEventQueue::PutEvent(nsIRunnable* aRun
     mOffsetTail = 0;
   }
 
   event.swap(mTail->mEvents[mOffsetTail]);
   ++mOffsetTail;
   LOG(("EVENTQ(%p): notify\n", this));
   mon.NotifyAll();
 }
+
+size_t
+nsEventQueue::Count()
+{
+  ReentrantMonitorAutoEnter mon(mReentrantMonitor);
+
+  // It is obvious count is 0 when the queue is empty.
+  if (!mHead) {
+    return 0;
+  }
+
+  /* How we count the number of events in the queue:
+   * 1. Let pageCount(x, y) denote the number of pages excluding the tail page
+   *    where x is the index of head page and y is the index of the tail page.
+   * 2. Then we have pageCount(x, y) = y - x.
+   *
+   * Ex: pageCount(0, 0) = 0 where both head and tail pages point to page 0.
+   *     pageCount(0, 1) = 1 where head points to page 0 and tail points page 1.
+   *
+   * 3. number of events = (EVENTS_PER_PAGE * pageCount(x, y))
+   *      - (empty slots in head page) + (non-empty slots in tail page)
+   *      = (EVENTS_PER_PAGE * pageCount(x, y)) - mOffsetHead + mOffsetTail
+   */
+
+  int count = -mOffsetHead;
+
+  // Compute (EVENTS_PER_PAGE * pageCount(x, y))
+  for (Page* page = mHead; page != mTail; page = page->mNext) {
+    count += EVENTS_PER_PAGE;
+  }
+
+  count += mOffsetTail;
+  MOZ_ASSERT(count >= 0);
+
+  return count;
+}
--- a/xpcom/threads/nsEventQueue.h
+++ b/xpcom/threads/nsEventQueue.h
@@ -46,16 +46,18 @@ public:
   }
 
   // Expose the event queue's monitor for "power users"
   ReentrantMonitor& GetReentrantMonitor()
   {
     return mReentrantMonitor;
   }
 
+  size_t Count();
+
 private:
 
   bool IsEmpty()
   {
     return !mHead || (mHead == mTail && mOffsetHead == mOffsetTail);
   }
 
   enum
--- a/xpcom/threads/nsThreadPool.cpp
+++ b/xpcom/threads/nsThreadPool.cpp
@@ -77,17 +77,20 @@ nsThreadPool::PutEvent(nsIRunnable* aEve
     if (NS_WARN_IF(mShutdown)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
     LOG(("THRD-P(%p) put [%d %d %d]\n", this, mIdleCount, mThreads.Count(),
          mThreadLimit));
     MOZ_ASSERT(mIdleCount <= (uint32_t)mThreads.Count(), "oops");
 
     // Make sure we have a thread to service this event.
-    if (mIdleCount == 0 && mThreads.Count() < (int32_t)mThreadLimit) {
+    if (mThreads.Count() < (int32_t)mThreadLimit &&
+        // Spawn a new thread if we don't have enough idle threads to serve
+        // pending events immediately.
+        mEvents.Count() >= mIdleCount) {
       spawnThread = true;
     }
 
     mEvents.PutEvent(aEvent);
     stackSize = mStackSize;
   }
 
   LOG(("THRD-P(%p) put [spawn=%d]\n", this, spawnThread));