Bug 1407810 - Tweak atomics in MultiWriterQueue - r=jwwang
This queue may be used heavily when multiple threads are running media code
that logs thousands of messages per second, so using less strict memory
ordering can help with speed.
Benchmarks show an improvement of up to twice the queueing speed in some
situations.
MozReview-Commit-ID: 70UOL8XAZGp
--- a/dom/media/doctor/MultiWriterQueue.h
+++ b/dom/media/doctor/MultiWriterQueue.h
@@ -295,17 +295,20 @@ private:
mValid = false;
return true;
}
private:
T mT;
// mValid should be atomically changed to true *after* mT has been written,
// so that the reader can only see valid data.
- Atomic<bool> mValid{ false };
+ // ReleaseAcquire, because when set to `true`, we want the just-written mT
+ // to be visible to the thread reading this `true`; and when set to `false`,
+ // we want the previous reads to have completed.
+ Atomic<bool, ReleaseAcquire> mValid{ false };
};
// Buffer contains a sequence of BufferedElements starting at a specific
// index, and it points to the next-older buffer (if any).
class Buffer
{
public:
// Constructor of the very first buffer.
@@ -442,34 +445,44 @@ private:
}
}
}
}
// Index of the next element to write. Modified when an element index is
// claimed for a push. If the last element of a buffer is claimed, that push
// will be responsible for adding a new head buffer.
- Atomic<Index::ValueType> mNextElementToWrite{ 0 };
+ // Relaxed, because there is no synchronization based on this variable, each
+ // thread just needs to get a different value, and will then write different
+ // things (which themselves have some atomic validation before they may be
+ // read elsewhere, independent of this `mNextElementToWrite`.)
+ Atomic<Index::ValueType, Relaxed> mNextElementToWrite{ 0 };
// Index that a live recent buffer reaches. If a push claims a lesser-or-
// equal number, the corresponding buffer is guaranteed to still be alive:
// - It will have been created before this index was updated,
// - It will not be destroyed until all its values have been written,
// including the one that just claimed a position within it.
// Also, the push that claims this exact number is responsible for adding the
// next buffer and updating this value accordingly.
- Atomic<Index::ValueType> mBuffersCoverAtLeastUpTo;
+ // ReleaseAcquire, because when set to a certain value, the just-created
+ // buffer covering the new range must be visible to readers.
+ Atomic<Index::ValueType, ReleaseAcquire> mBuffersCoverAtLeastUpTo;
// Pointer to the most recent buffer. Never null.
// This is the most recent of a deque of yet-unread buffers.
// Only modified when adding a new head buffer.
- Atomic<Buffer*> mMostRecentBuffer;
+ // ReleaseAcquire, because when modified, the just-created new buffer must be
+ // visible to readers.
+ Atomic<Buffer*, ReleaseAcquire> mMostRecentBuffer;
// Stack of reusable buffers.
- Atomic<Buffer*> mReusableBuffers;
+ // ReleaseAcquire, because when modified, the just-added buffer must be
+ // visible to readers.
+ Atomic<Buffer*, ReleaseAcquire> mReusableBuffers;
// Template-provided locking mechanism to protect PopAll()-only member
// variables below.
ReaderLocking mReaderLocking;
// Pointer to the oldest buffer, which contains the new element to be popped.
// Never null.
Buffer* mOldestBuffer;
@@ -513,18 +526,20 @@ private:
int operator--()
{
int count = int(--mCount);
// printf("--[%p] -> %d\n", this, count);
return count;
}
private:
- Atomic<int> mCount;
- Atomic<int> mWatermark;
+ // Relaxed, as these are just gathering stats, so consistency is not
+ // critical.
+ Atomic<int, Relaxed> mCount;
+ Atomic<int, Relaxed> mWatermark;
};
// All buffers in the mMostRecentBuffer deque.
AtomicCountAndWatermark mLiveBuffersStats;
// All buffers in the mReusableBuffers stack.
AtomicCountAndWatermark mReusableBuffersStats;
// All allocated buffers (sum of above).
AtomicCountAndWatermark mAllocatedBuffersStats;
};