summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qmutex.cpp
diff options
context:
space:
mode:
authorBradley T. Hughes <bradley.hughes@nokia.com>2010-10-01 07:50:22 (GMT)
committerBradley T. Hughes <bradley.hughes@nokia.com>2010-12-20 15:49:51 (GMT)
commit3b6a84de5c5ed2e1970ad2b396292babb9173227 (patch)
tree897d4451642314f832f0ebdd3c6d408e446d44e2 /src/corelib/thread/qmutex.cpp
parent8a7b5aca7b6575013a4e4ee9b99808d25edf6fdf (diff)
downloadQt-3b6a84de5c5ed2e1970ad2b396292babb9173227.zip
Qt-3b6a84de5c5ed2e1970ad2b396292babb9173227.tar.gz
Qt-3b6a84de5c5ed2e1970ad2b396292babb9173227.tar.bz2
Optimize adaptive spinning mutex code
Change the adaptive spinning code to be elapsed time based instead of iteration based. The new approach will quickly reduce the amount of allowed spinning time when it detects that lock contention is resolved by waiting instead of spinning. We get better results by dynamically adjusting for elapsed running time instead of trying to fine tune iteration counts that won't work for all CPU types, speeds, etc. From observation, lock performance suffers if the spin time is higher than the minimum wait time. Because of this, QMutex never increases the spin time, it only reduces the spin time to the minimum observed wait time. For very long wait times, we disable spinning completely (and always resolve contention by waiting). Use QThread::yieldCurrentThread() when spinning on a contended mutex. Comment from the code: be a good citizen... yielding lets something else run if there is something to run, but may also relieve memory pressure if not. Reviewed-by: joao
Diffstat (limited to 'src/corelib/thread/qmutex.cpp')
-rw-r--r--src/corelib/thread/qmutex.cpp36
1 files changed, 20 insertions, 16 deletions
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
index 54b3ed4..72f87b7 100644
--- a/src/corelib/thread/qmutex.cpp
+++ b/src/corelib/thread/qmutex.cpp
@@ -45,6 +45,7 @@
#ifndef QT_NO_THREAD
#include "qatomic.h"
+#include "qelapsedtimer.h"
#include "qthread.h"
#include "qmutex_p.h"
@@ -439,31 +440,34 @@ void QMutex::lockInternal()
return;
}
- int spinCount = 0;
- int lastSpinCount = d->lastSpinCount;
-
- enum { AdditionalSpins = 20, SpinCountPenalizationDivisor = 4 };
- const int maximumSpinCount = lastSpinCount + AdditionalSpins;
-
+ QElapsedTimer elapsedTimer;
+ elapsedTimer.start();
do {
- if (spinCount++ > maximumSpinCount) {
- // didn't get the lock, wait for it
+ if (elapsedTimer.hasExpired(d->maximumSpinTime)) {
+ // didn't get the lock, wait for it, since we're not going to gain anything by spinning more
+ int spinTime = elapsedTimer.restart();
bool isLocked = d->wait();
Q_ASSERT_X(isLocked, "QMutex::lock",
"Internal error, infinite wait has timed out.");
Q_UNUSED(isLocked);
- // decrease the lastSpinCount since we didn't actually get the lock by spinning
- spinCount = -d->lastSpinCount / SpinCountPenalizationDivisor;
- break;
+ int maximumSpinTime = d->maximumSpinTime;
+ int waitTime = elapsedTimer.elapsed();
+ // adjust the spin count when spinning does not benefit contention performance
+ if (spinTime + waitTime > QMutexPrivate::MaximumSpinTimeThreshold) {
+ // long waits, stop spinning
+ d->maximumSpinTime = 0;
+ } else if (waitTime < maximumSpinTime) {
+ // never spin more than the minimum wait time (otherwise we may perform worse)
+ d->maximumSpinTime = waitTime;
+ }
+ return;
}
+ // be a good citizen... yielding lets something else run if there is something to run, but may also relieve memory pressure if not
+ QThread::yieldCurrentThread();
} while (d->contenders != 0 || !d->contenders.testAndSetAcquire(0, 1));
- // adjust the last spin lock count
- lastSpinCount = d->lastSpinCount;
- d->lastSpinCount = spinCount >= 0
- ? qMax(lastSpinCount, spinCount)
- : lastSpinCount + spinCount;
+ // spinning is working, do not change the spin time
}
/*!