News for hackers. Stuff that matters.
Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by p0nce on Wednesday January 01, 2020 @05:58AM (#21927571)
A typical mutex has essentially two very different costs depending on whether the lock is already taken by another thread:

- Uncontended case + mutex: no syscall, fast path is basically the cost of the memory barrier.

- Uncontended case + spinlock: no syscall, fast path is basically the cost of the memory barrier. Essentially same cost than a mutex, sometimes a tiny bit cheaper but _if your lock isn't contented in the first place it will be very hard to measure, since not being a bottleneck_.

- Contended case + mutex: thread is waiting in the mutex list without taking CPU. Hyper-threading may also give hand immediately to another thread, perhaps lucky enough to run. The OS mutex may have a bit of capped spinning before waiting in the scheduler.

- Contended case + spinlock: Anything goes. CPU gets consumed just for spinning. Wrong thread might get priority. If you are lucky you did put a HLT instruction to mitigate the complete disaster that spinlocks are, perhaps it even does something. Your consolation: no syscall or thread pausing... because they are spinning.

tl;dr: spinlock are worse in the contended case and essentially the same performance in the uncontended case against your typical OS mutex. SPINLOCKS WON'T YIELD THE EXPECTED BENEFITS, USE REGULAR OS MUTEXES.

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by gpderetta on Wednesday January 01, 2020 @07:48AM (#21927824)
A problem with pthread mutex and std::mutex is that they are often very large which is problematic for very fine grained locking, while you only need one bit for a spin lock and can be embedded in other objects (say, a pointer) as long as you can spare one bit.

Of course you can roll your own proper mutex if you want to get your hands dirty with futexes. IIRC you only need two bits (one waiter bit and one locked bit).

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by shaklee3 on Wednesday January 01, 2020 @03:27PM (#21930374)
Not sure what you mean by a single bit for a spin lock. It's a cas operation, which doesn't operate on a single bit.

Edit: I think I see what you're getting at, but the point from the article about cache invalidation for the naive implementation still is valid.

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by Matthias247 on Thursday January 02, 2020 @05:20AM (#21934194)
You can do it with a single bit: Read the whole unit (eg 64 bit), flip the big you need, and then do a Compare exchange which tries to replace the whole retrieved thingy with the modified one.

That obviously requires you to read the current state upfront, whereas a plan compare exchange from 0 to 1 on 32/64Bit avoids that.

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by p0nce on Thursday January 02, 2020 @05:39AM (#21934297)
That doesn't work since this isn't atomic anymore.
Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by gpderetta on Thursday January 02, 2020 @09:49AM (#21935751)
Of course it is. The mutating operation is the CAS which requires the existing value anyway.
Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by p0nce on Thursday January 02, 2020 @10:01AM (#21935841)
Mmmm... I had to write this down.

    0: CAS_bit_n(void* adr, int n)
    1: while(true):
    2:   old <- read unit(adr)
    3:   new <- compute-new-value-with-changed-bit-n(old, n)
    4:   if CAS(adr, old, new):
    5:     break
    6:   else
    7:     continue
Indeed it seems it is correct if you retry.
Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by gpderetta on Thursday January 02, 2020 @10:59AM (#21936430)
In fact normally a failed CAS (know as IBM-style CAS) will update the old value anyway so no need to reload old inside the loop; in c++11:

  auto old = addr.load(std::memory_order_relaxed);
  while(not addr.compare_exchange_weak(old, old|1))
     ;
In fact you can set old to some random value and it will still convergence to the correct value, but is of course suboptimal.
Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by p0nce on Thursday January 02, 2020 @11:35AM (#21936822)
Interesting! Now I'm interested if there has been one usage case for the single bit spinlock in practice. It seems appropriate if writes aren't too numerous (well leaving aside speed concerns). Or a case where std::mutex was too large?

I've always wondered how the wait list was implemented.

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by gpderetta on Thursday January 02, 2020 @11:58AM (#21937066)
Think about a concurrent hashtable using chaining for collision resolution; distinct bucket chains can be updated concurrently, so you need a per bucket lock but you do not want a full std::mutex as it would increase the size of the bucket table by a large integer multiple. Instead, borrowing one bit from the chain pointer (you can easily arrange the least significant bit of the pointer to be always zero) is pretty much free.

Note: I wrote the above from the top of my head, I haven't actually had the need to implement a concurrent hashtable yet.

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by ketzu on Wednesday January 01, 2020 @07:04AM (#21927719)
I believe the supposed beneficial case of blocking vs spinning can't be captured well by those categories, as it's "contended, but not for long" and "contended, but for longer". That's the reason for the "may have a bit of capped spinning before waiting in the scheduler".
Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by p0nce on Wednesday January 01, 2020 @07:53AM (#21927840)
Honestly I'm not so sure about the case for spinning; in x264 source you can see CRITICAL_SECTION is tuned to have a spincount of 0. This is a very optimized, multi-threaded app. They didnt want any spinning.

https://github.com/mirror/x264/blob/d4099dd4c722f52c4f3c1457...

In my own attempts I've been unable to measure the impact of the spinlock count, and took spincount = 40 like in https://trac.webkit.org/browser/webkit/trunk/Source/WTF/wtf/... ...who also took measurement from elsewhere. The default is 4000 on Windows!

I don't argue spinning isn't valuable ; what I say is that it's necessarily never the bottleneck (else your task size really is too small).

After all, if you spend all your time in synchronization, that means you probably need to have bigger tasks, not more clever synchronization primitives.

Re:Measuring mutexes, spinlocks and how bad the Linux schedu... by temac on Wednesday January 01, 2020 @07:23AM (#21927763)
Besides OS where they are often needed, spinlocks can have their place in a few very specific (and most of the time isolated on dedicated computers), but I agree that too many people are trying to play with them now, without enough knowledge.