Re: adaptive spinning: fall back to sleep / block?

From: Mateusz Guzik <mjguzik_at_gmail.com>
Date: Mon, 17 May 2021 10:23:20 UTC
On 4/9/21, Andriy Gapon <avg@freebsd.org> wrote:
>
>
> Until I recently looked at the actual code I was under an impression that
> the adaptive spinning is bounded and that after some time / number of spins
> a
> thread would go to a sleep queue or a turnstile.  But it looks that the
> spinning
> is actually unbounded as long as its conditions hold (some other thread owns
> the
> lock and that thread is running, the owner could be changing too).
>
> In my opinion, it does not make sense to spin for "too long".
> If there was not an opportunity to take a lock quickly, then it's better to
> block waiting for it rather than keep occupying a processor.  For instance,
> the
> spinning can prevent another runnable thread from running.
>
> I think that if a lock is heavily contended or its hold times are on the
> longer
> side (or both), then the adaptive spinning can make the system behavior
> (performance, responsiveness) worse.
>
> Finally, I was under an impression that 'adaptive' meant some heuristic on
> whether and when to do the spinning.  _A lock owner is running_ seems to be
> too
> simple to qualify as 'adaptive'.
>
> As an example, this looks like a relatively sophisticated implementation of
> the
> "adaptiveness":
> http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/runtime/objectMonitor.cpp#l1919
> But, JIMHO, simply having a hard limit on the spin count would be better
> than
> what we have now.
>

There is no clear cut answer to this that I'm aware of. Ultimately all
behavior in face of contention is about damage control.

It's not hard to make a counter point to going off cpu after a timeout:
1. going off cpu is also a serializing operation, that is threads can
content on doing so, albeit less than on the original lock
2. existence of blocked waiters makes it more expensive to release the
lock, making contention worse and even then it is unclear what wake up
policy you would like to enact (e.g., do you hand off the lock to the
oldest sleeper? do you wake everyone and let them fight it out? all
choices suffer problems)
3. consider a thread which holds foo_lock and now contends on bar_lock
and decides to go off cpu. if there is a thread which waits on
foo_lock, it will also go off cpu. This kind of behavior easily leads
to dramatic collapse in throughput, even on top of whatever problems
which are present due to contention to begin with.

Now, locking primitives in the FreeBSD kernel are problematic in at
least 3 ways:
1. there are no fairness guarantees, for example a constant stream of
threads can end up excluding some threads for a long time
2. there is no support for locking under kvm (as in the kernel does
not take advantage of it)
3. rw locking is using cas loops instead of add

imo, if doing anything serious with locks, the 3 above problems needs
to be solved, in that order.

I can't stress enough the lack of fairness, which arbitrary going to
sleep will only exacerbate. As noted earlier, I don't know if timeout
sleep is an inherently good or bad idea, but I'm confident playing
with it in the situation is on the bad side.

-- 
Mateusz Guzik <mjguzik gmail.com>