12

Well the title basically says it all, with the small addition that I would really like to know when to use them. And it might be simple enough - I've read the documentation for them both, still can't tell the difference much.

There are answers like this here that basically say:

Yielding also was useful for busy waiting...

I can't agree much with them for the simple reason that ForkJoinPool uses Thread::yield internally and that is a pretty recent addition in the jdk world.

The thing that really bothers me is usages like this in jdk too (StampledLock::tryDecReaderOverflow):

    else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0)
        Thread.yield();
    else
        Thread.onSpinWait();
    return 0L;

So it seems there are cases when one would be preferred over the other. And no, I don't have an actual example where I might need this - the only one I actually used was Thread::onSpinWait because 1) I happened to busy wait 2) the name is pretty much self explanatory that I should have used it in the busy spin.

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • 1
    Can you describe a scenario in which you'd need to spin wait? As a Java user not implementer, I've never needed to. In kernel programming, sure, spin locks are ubiquitous. In Java, though? I can't think of a use case where I'd spin wait rather than use a lock, monitor, or async callback. (I'm not being snarky. Asking which one to use is perhaps the wrong question if the answer is hardly anyone should be using either of them.) – John Kugelman May 09 '19 at 10:00
  • 1
    Possible duplicate of [onSpinWait​() method of Thread class](https://stackoverflow.com/questions/44622384/onspinwait-method-of-thread-class) – Torben May 09 '19 at 10:00
  • @JohnKugelman I never _needed_ to - I just knew that the method existed and it's documentation had an example that matched very closely to what I had, thus decided to use it, _in hopes_ that it will somehow improve performance. I also never needed to busy spin in the first place - it just felt like something I should have absolutely tried. – Eugene May 09 '19 at 10:05
  • @Torben considering that I gave one of the answers in that question, I don't think it's a duplicate. – Eugene May 09 '19 at 10:05
  • 1
    The API example shows a loop spinning on a volatile flag. That's fine if you're implementing a low-level locking primitive. Odds are that most people reading it are not. They should stay far, far away from such an idiom. Spinning on a variable is only for total experts and total beginners. – John Kugelman May 09 '19 at 10:11
  • @JohnKugelman hmm.. I would be in the second group, it just felt like something I should have tried and it's not that complicated if all that busy spin does is act like a monitor – Eugene May 09 '19 at 10:18
  • @JohnKugelman It's indispensable for real-time applications that rely on consistent update rates with sub-millisecond precision (such as video games) – Julian Durchholz Aug 01 '22 at 22:36

1 Answers1

12

When blocking a thread, there are a few strategies to choose from: spin, wait() / notify(), or a combination of both. Pure spinning on a variable is a very low latency strategy but it can starve other threads that are contending for CPU time. On the other hand, wait() / notify() will free up the CPU for other threads but can cost thousands of CPU cycles in latency when descheduling/scheduling threads.

So how can we avoid pure spinning as well as the overhead associated with descheduling and scheduling the blocked thread?

Thread.yield() is a hint to the thread scheduler to give up its time slice if another thread with equal or higher priority is ready. This avoids pure spinning but doesn't avoid the overhead of rescheduling the thread.

The latest addition is Thread.onSpinWait() which inserts architecture-specific instructions to hint the processor that a thread is in a spin loop. On x86, this is probably the PAUSE instruction, on aarch64, this is the YIELD instruction.

What's the use of these instructions? In a pure spin loop, the processor will speculatively execute the loop over and over again, filling up the pipeline. When the variable the thread is spinning on finally changes, all that speculative work will be thrown out due to memory order violation. What a waste!

A hint to the processor could prevent the pipeline from speculatively executing the spin loop until prior memory instructions are committed. In the context of SMT (hyperthreading), this is useful as the pipeline will be freed up for other hardware threads.

Eric
  • 906
  • 7
  • 13
  • 5
    So `onSpinWait()` is the right thing if we expect the other thread to already run on a different CPU (core) fulfilling the condition whereas `yield()` is the right thing if we expect the other thread not having CPU time. Unfortunately, we can’t know, so the example code shown in the question uses some random based heuristic to decide when to invoke which method. – Holger May 09 '19 at 16:38