Skip to content

Commit ee7b2e9

Browse files
committed
8330051: Small ObjectMonitor spinning code cleanups
Reviewed-by: dcubed, eosterlund, fbredberg
1 parent 3e185c7 commit ee7b2e9

File tree

2 files changed

+104
-75
lines changed

2 files changed

+104
-75
lines changed

src/hotspot/share/runtime/objectMonitor.cpp

Lines changed: 96 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,6 @@
108108

109109
#endif // ndef DTRACE_ENABLED
110110

111-
// Tunables ...
112-
// The knob* variables are effectively final. Once set they should
113-
// never be modified hence. Consider using __read_mostly with GCC.
114-
115-
int ObjectMonitor::Knob_SpinLimit = 5000; // derived by an external tool -
116-
117-
static int Knob_Bonus = 100; // spin success bonus
118-
static int Knob_BonusB = 100; // spin success bonus
119-
static int Knob_Penalty = 200; // spin failure penalty
120-
static int Knob_Poverty = 1000;
121-
static int Knob_FixedSpin = 0;
122-
static int Knob_PreSpin = 10; // 20-100 likely better
123-
124111
DEBUG_ONLY(static volatile bool InitDone = false;)
125112

126113
OopStorage* ObjectMonitor::_oop_storage = nullptr;
@@ -406,7 +393,7 @@ bool ObjectMonitor::enter(JavaThread* current) {
406393
// transitions. The following spin is strictly optional ...
407394
// Note that if we acquire the monitor from an initial spin
408395
// we forgo posting JVMTI events and firing DTRACE probes.
409-
if (TrySpin(current) > 0) {
396+
if (TrySpin(current)) {
410397
assert(owner_raw() == current, "must be current: owner=" INTPTR_FORMAT, p2i(owner_raw()));
411398
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
412399
assert(object()->mark() == markWord::encode(this),
@@ -535,18 +522,18 @@ bool ObjectMonitor::enter(JavaThread* current) {
535522
// Caveat: TryLock() is not necessarily serializing if it returns failure.
536523
// Callers must compensate as needed.
537524

538-
int ObjectMonitor::TryLock(JavaThread* current) {
525+
ObjectMonitor::TryLockResult ObjectMonitor::TryLock(JavaThread* current) {
539526
void* own = owner_raw();
540-
if (own != nullptr) return 0;
527+
if (own != nullptr) return TryLockResult::HasOwner;
541528
if (try_set_owner_from(nullptr, current) == nullptr) {
542529
assert(_recursions == 0, "invariant");
543-
return 1;
530+
return TryLockResult::Success;
544531
}
545532
// The lock had been free momentarily, but we lost the race to the lock.
546533
// Interference -- the CAS failed.
547534
// We can either return -1 or retry.
548535
// Retry doesn't make as much sense because the lock was just acquired.
549-
return -1;
536+
return TryLockResult::Interference;
550537
}
551538

552539
// Deflate the specified ObjectMonitor if not in-use. Returns true if it
@@ -723,7 +710,7 @@ void ObjectMonitor::EnterI(JavaThread* current) {
723710
assert(current->thread_state() == _thread_blocked, "invariant");
724711

725712
// Try the lock - TATAS
726-
if (TryLock (current) > 0) {
713+
if (TryLock(current) == TryLockResult::Success) {
727714
assert(_succ != current, "invariant");
728715
assert(owner_raw() == current, "invariant");
729716
assert(_Responsible != current, "invariant");
@@ -757,7 +744,7 @@ void ObjectMonitor::EnterI(JavaThread* current) {
757744
// to the owner. This has subtle but beneficial affinity
758745
// effects.
759746

760-
if (TrySpin(current) > 0) {
747+
if (TrySpin(current)) {
761748
assert(owner_raw() == current, "invariant");
762749
assert(_succ != current, "invariant");
763750
assert(_Responsible != current, "invariant");
@@ -794,7 +781,7 @@ void ObjectMonitor::EnterI(JavaThread* current) {
794781

795782
// Interference - the CAS failed because _cxq changed. Just retry.
796783
// As an optional optimization we retry the lock.
797-
if (TryLock (current) > 0) {
784+
if (TryLock(current) == TryLockResult::Success) {
798785
assert(_succ != current, "invariant");
799786
assert(owner_raw() == current, "invariant");
800787
assert(_Responsible != current, "invariant");
@@ -847,7 +834,9 @@ void ObjectMonitor::EnterI(JavaThread* current) {
847834

848835
for (;;) {
849836

850-
if (TryLock(current) > 0) break;
837+
if (TryLock(current) == TryLockResult::Success) {
838+
break;
839+
}
851840
assert(owner_raw() != current, "invariant");
852841

853842
// park self
@@ -862,7 +851,9 @@ void ObjectMonitor::EnterI(JavaThread* current) {
862851
current->_ParkEvent->park();
863852
}
864853

865-
if (TryLock(current) > 0) break;
854+
if (TryLock(current) == TryLockResult::Success) {
855+
break;
856+
}
866857

867858
if (try_set_owner_from(DEFLATER_MARKER, current) == DEFLATER_MARKER) {
868859
// Cancelled the in-progress async deflation by changing owner from
@@ -895,7 +886,9 @@ void ObjectMonitor::EnterI(JavaThread* current) {
895886
// We can defer clearing _succ until after the spin completes
896887
// TrySpin() must tolerate being called with _succ == current.
897888
// Try yet another round of adaptive spinning.
898-
if (TrySpin(current) > 0) break;
889+
if (TrySpin(current)) {
890+
break;
891+
}
899892

900893
// We can find that we were unpark()ed and redesignated _succ while
901894
// we were spinning. That's harmless. If we iterate and call park(),
@@ -994,8 +987,9 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
994987
guarantee(v == ObjectWaiter::TS_ENTER || v == ObjectWaiter::TS_CXQ, "invariant");
995988
assert(owner_raw() != current, "invariant");
996989

997-
if (TryLock(current) > 0) break;
998-
if (TrySpin(current) > 0) break;
990+
if (TrySpin(current)) {
991+
break;
992+
}
999993

1000994
{
1001995
OSThreadContendState osts(current->osthread());
@@ -1012,7 +1006,9 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
10121006
// Try again, but just so we distinguish between futile wakeups and
10131007
// successful wakeups. The following test isn't algorithmically
10141008
// necessary, but it helps us maintain sensible statistics.
1015-
if (TryLock(current) > 0) break;
1009+
if (TryLock(current) == TryLockResult::Success) {
1010+
break;
1011+
}
10161012

10171013
// The lock is still contested.
10181014
// Keep a tally of the # of futile wakeups.
@@ -1854,39 +1850,78 @@ void ObjectMonitor::notifyAll(TRAPS) {
18541850
// hysteresis control to damp the transition rate between spinning and
18551851
// not spinning.
18561852

1857-
// Spinning: Fixed frequency (100%), vary duration
1858-
int ObjectMonitor::TrySpin(JavaThread* current) {
1859-
// Dumb, brutal spin. Good for comparative measurements against adaptive spinning.
1860-
int ctr = Knob_FixedSpin;
1861-
if (ctr != 0) {
1862-
while (--ctr >= 0) {
1863-
if (TryLock(current) > 0) return 1;
1864-
SpinPause();
1853+
int ObjectMonitor::Knob_SpinLimit = 5000; // derived by an external tool
1854+
1855+
static int Knob_Bonus = 100; // spin success bonus
1856+
static int Knob_Penalty = 200; // spin failure penalty
1857+
static int Knob_Poverty = 1000;
1858+
static int Knob_FixedSpin = 0;
1859+
static int Knob_PreSpin = 10; // 20-100 likely better, but it's not better in my testing.
1860+
1861+
inline static int adjust_up(int spin_duration) {
1862+
int x = spin_duration;
1863+
if (x < ObjectMonitor::Knob_SpinLimit) {
1864+
if (x < Knob_Poverty) {
1865+
x = Knob_Poverty;
18651866
}
1866-
return 0;
1867+
return x + Knob_Bonus;
1868+
} else {
1869+
return spin_duration;
18671870
}
1871+
}
1872+
1873+
inline static int adjust_down(int spin_duration) {
1874+
// TODO: Use an AIMD-like policy to adjust _SpinDuration.
1875+
// AIMD is globally stable.
1876+
int x = spin_duration;
1877+
if (x > 0) {
1878+
// Consider an AIMD scheme like: x -= (x >> 3) + 100
1879+
// This is globally sample and tends to damp the response.
1880+
x -= Knob_Penalty;
1881+
if (x < 0) { x = 0; }
1882+
return x;
1883+
} else {
1884+
return spin_duration;
1885+
}
1886+
}
18681887

1869-
for (ctr = Knob_PreSpin + 1; --ctr >= 0;) {
1870-
if (TryLock(current) > 0) {
1871-
// Increase _SpinDuration ...
1872-
// Note that we don't clamp SpinDuration precisely at SpinLimit.
1873-
// Raising _SpurDuration to the poverty line is key.
1874-
int x = _SpinDuration;
1875-
if (x < Knob_SpinLimit) {
1876-
if (x < Knob_Poverty) x = Knob_Poverty;
1877-
_SpinDuration = x + Knob_BonusB;
1888+
bool ObjectMonitor::short_fixed_spin(JavaThread* current, int spin_count, bool adapt) {
1889+
for (int ctr = 0; ctr < spin_count; ctr++) {
1890+
TryLockResult status = TryLock(current);
1891+
if (status == TryLockResult::Success) {
1892+
if (adapt) {
1893+
_SpinDuration = adjust_up(_SpinDuration);
18781894
}
1879-
return 1;
1895+
return true;
1896+
} else if (status == TryLockResult::Interference) {
1897+
break;
18801898
}
18811899
SpinPause();
18821900
}
1901+
return false;
1902+
}
1903+
1904+
// Spinning: Fixed frequency (100%), vary duration
1905+
bool ObjectMonitor::TrySpin(JavaThread* current) {
1906+
1907+
// Dumb, brutal spin. Good for comparative measurements against adaptive spinning.
1908+
int knob_fixed_spin = Knob_FixedSpin; // 0 (don't spin: default), 2000 good test
1909+
if (knob_fixed_spin > 0) {
1910+
return short_fixed_spin(current, knob_fixed_spin, false);
1911+
}
18831912

18841913
// Admission control - verify preconditions for spinning
18851914
//
18861915
// We always spin a little bit, just to prevent _SpinDuration == 0 from
18871916
// becoming an absorbing state. Put another way, we spin briefly to
18881917
// sample, just in case the system load, parallelism, contention, or lock
18891918
// modality changed.
1919+
1920+
int knob_pre_spin = Knob_PreSpin; // 10 (default), 100, 1000 or 2000
1921+
if (short_fixed_spin(current, knob_pre_spin, true)) {
1922+
return true;
1923+
}
1924+
18901925
//
18911926
// Consider the following alternative:
18921927
// Periodically set _SpinDuration = _SpinLimit and try a long/full
@@ -1895,8 +1930,8 @@ int ObjectMonitor::TrySpin(JavaThread* current) {
18951930
// This takes us into the realm of 1-out-of-N spinning, where we
18961931
// hold the duration constant but vary the frequency.
18971932

1898-
ctr = _SpinDuration;
1899-
if (ctr <= 0) return 0;
1933+
int ctr = _SpinDuration;
1934+
if (ctr <= 0) return false;
19001935

19011936
// We're good to spin ... spin ingress.
19021937
// CONSIDER: use Prefetch::write() to avoid RTS->RTO upgrades
@@ -1928,7 +1963,7 @@ int ObjectMonitor::TrySpin(JavaThread* current) {
19281963
// might update the poll values and we could be in a thread_blocked
19291964
// state here which is not allowed so just check the poll.
19301965
if (SafepointMechanism::local_poll_armed(current)) {
1931-
goto Abort; // abrupt spin egress
1966+
break;
19321967
}
19331968
SpinPause();
19341969
}
@@ -1960,26 +1995,21 @@ int ObjectMonitor::TrySpin(JavaThread* current) {
19601995
// If we acquired the lock early in the spin cycle it
19611996
// makes sense to increase _SpinDuration proportionally.
19621997
// Note that we don't clamp SpinDuration precisely at SpinLimit.
1963-
int x = _SpinDuration;
1964-
if (x < Knob_SpinLimit) {
1965-
if (x < Knob_Poverty) x = Knob_Poverty;
1966-
_SpinDuration = x + Knob_Bonus;
1967-
}
1968-
return 1;
1998+
_SpinDuration = adjust_up(_SpinDuration);
1999+
return true;
19692000
}
19702001

19712002
// The CAS failed ... we can take any of the following actions:
19722003
// * penalize: ctr -= CASPenalty
1973-
// * exit spin with prejudice -- goto Abort;
2004+
// * exit spin with prejudice -- abort without adapting spinner
19742005
// * exit spin without prejudice.
19752006
// * Since CAS is high-latency, retry again immediately.
1976-
prv = ox;
1977-
goto Abort;
2007+
break;
19782008
}
19792009

19802010
// Did lock ownership change hands ?
19812011
if (ox != prv && prv != nullptr) {
1982-
goto Abort;
2012+
break;
19832013
}
19842014
prv = ox;
19852015

@@ -1989,30 +2019,23 @@ int ObjectMonitor::TrySpin(JavaThread* current) {
19892019
}
19902020

19912021
// Spin failed with prejudice -- reduce _SpinDuration.
1992-
// TODO: Use an AIMD-like policy to adjust _SpinDuration.
1993-
// AIMD is globally stable.
1994-
{
1995-
int x = _SpinDuration;
1996-
if (x > 0) {
1997-
// Consider an AIMD scheme like: x -= (x >> 3) + 100
1998-
// This is globally sample and tends to damp the response.
1999-
x -= Knob_Penalty;
2000-
if (x < 0) x = 0;
2001-
_SpinDuration = x;
2002-
}
2022+
if (ctr < 0) {
2023+
_SpinDuration = adjust_down(_SpinDuration);
20032024
}
20042025

2005-
Abort:
20062026
if (_succ == current) {
20072027
_succ = nullptr;
20082028
// Invariant: after setting succ=null a contending thread
20092029
// must recheck-retry _owner before parking. This usually happens
20102030
// in the normal usage of TrySpin(), but it's safest
20112031
// to make TrySpin() as foolproof as possible.
20122032
OrderAccess::fence();
2013-
if (TryLock(current) > 0) return 1;
2033+
if (TryLock(current) == TryLockResult::Success) {
2034+
return true;
2035+
}
20142036
}
2015-
return 0;
2037+
2038+
return false;
20162039
}
20172040

20182041

src/hotspot/share/runtime/objectMonitor.hpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,8 +353,14 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
353353
void EnterI(JavaThread* current);
354354
void ReenterI(JavaThread* current, ObjectWaiter* current_node);
355355
void UnlinkAfterAcquire(JavaThread* current, ObjectWaiter* current_node);
356-
int TryLock(JavaThread* current);
357-
int TrySpin(JavaThread* current);
356+
357+
358+
enum class TryLockResult { Interference = -1, HasOwner = 0, Success = 1 };
359+
360+
TryLockResult TryLock(JavaThread* current);
361+
362+
bool TrySpin(JavaThread* current);
363+
bool short_fixed_spin(JavaThread* current, int spin_count, bool adapt);
358364
void ExitEpilog(JavaThread* current, ObjectWaiter* Wakee);
359365

360366
// Deflation support

0 commit comments

Comments
 (0)