Skip to content

Commit 790db30

Browse files
committed
Added MAX_SEMA_SPINS to traits to control semaphore spin-waiting (see issue #205)
1 parent 7912b95 commit 790db30

3 files changed

Lines changed: 17 additions & 19 deletions

File tree

blockingconcurrentqueue.h

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class BlockingConcurrentQueue
5656
// includes making the memory effects of construction visible, possibly with a
5757
// memory barrier).
5858
explicit BlockingConcurrentQueue(size_t capacity = 6 * BLOCK_SIZE)
59-
: inner(capacity), sema(create<LightweightSemaphore>(), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
59+
: inner(capacity), sema(create<LightweightSemaphore>(0, Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
6060
{
6161
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
6262
if (!sema) {
@@ -65,7 +65,7 @@ class BlockingConcurrentQueue
6565
}
6666

6767
BlockingConcurrentQueue(size_t minCapacity, size_t maxExplicitProducers, size_t maxImplicitProducers)
68-
: inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create<LightweightSemaphore>(), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
68+
: inner(minCapacity, maxExplicitProducers, maxImplicitProducers), sema(create<LightweightSemaphore>(0, Traits::MAX_SEMA_SPINS), &BlockingConcurrentQueue::template destroy<LightweightSemaphore>)
6969
{
7070
assert(reinterpret_cast<ConcurrentQueue*>((BlockingConcurrentQueue*)1) == &((BlockingConcurrentQueue*)1)->inner && "BlockingConcurrentQueue must have ConcurrentQueue as its first member");
7171
if (!sema) {
@@ -551,18 +551,11 @@ class BlockingConcurrentQueue
551551

552552

553553
private:
554-
template<typename U>
555-
static inline U* create()
556-
{
557-
auto p = (Traits::malloc)(sizeof(U));
558-
return p != nullptr ? new (p) U : nullptr;
559-
}
560-
561-
template<typename U, typename A1>
562-
static inline U* create(A1&& a1)
554+
template<typename U, typename A1, typename A2>
555+
static inline U* create(A1&& a1, A2&& a2)
563556
{
564-
auto p = (Traits::malloc)(sizeof(U));
565-
return p != nullptr ? new (p) U(std::forward<A1>(a1)) : nullptr;
557+
void* p = (Traits::malloc)(sizeof(U));
558+
return p != nullptr ? new (p) U(std::forward<A1>(a1), std::forward<A2>(a2)) : nullptr;
566559
}
567560

568561
template<typename U>

concurrentqueue.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,12 @@ struct ConcurrentQueueDefaultTraits
365365
// that this limit is enforced at the block level (for performance reasons), i.e.
366366
// it's rounded up to the nearest block size.
367367
static const size_t MAX_SUBQUEUE_SIZE = details::const_numeric_max<size_t>::value;
368+
369+
// The number of times to spin before sleeping when waiting on a semaphore.
370+
// Recommended values are on the order of 1000-10000 unless the number of
371+
// consumer threads exceeds the number of idle cores (in which case try 0-100).
372+
// Only affects instances of the BlockingConcurrentQueue.
373+
static const int MAX_SEMA_SPINS = 10000;
368374

369375

370376
#ifndef MCDBGQ_USE_RELACY

lightweightsemaphore.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,12 @@ class LightweightSemaphore
257257
private:
258258
std::atomic<ssize_t> m_count;
259259
details::Semaphore m_sema;
260+
int m_maxSpins;
260261

261262
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1)
262263
{
263264
ssize_t oldCount;
264-
// Is there a better way to set the initial spin count?
265-
// If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
266-
// as threads start hitting the kernel semaphore.
267-
int spin = 10000;
265+
int spin = m_maxSpins;
268266
while (--spin >= 0)
269267
{
270268
oldCount = m_count.load(std::memory_order_relaxed);
@@ -298,7 +296,7 @@ class LightweightSemaphore
298296
{
299297
assert(max > 0);
300298
ssize_t oldCount;
301-
int spin = 10000;
299+
int spin = m_maxSpins;
302300
while (--spin >= 0)
303301
{
304302
oldCount = m_count.load(std::memory_order_relaxed);
@@ -336,9 +334,10 @@ class LightweightSemaphore
336334
}
337335

338336
public:
339-
LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount)
337+
LightweightSemaphore(ssize_t initialCount = 0, int maxSpins = 10000) : m_count(initialCount), m_maxSpins(maxSpins)
340338
{
341339
assert(initialCount >= 0);
340+
assert(maxSpins >= 0);
342341
}
343342

344343
bool tryWait()

0 commit comments

Comments
 (0)