3 ms·
atomic.AddInt32 feels a lot like locking to me. You end up with a write barrier and all the associated consequences, so you can kill throughput (in a way that
by jrockway 5y ago
atomic.AddInt32 feels a lot like locking to me. You end up with a write barrier and all the associated consequences, so you can kill throughput (in a way that is more difficult to notice in the profiler, though you'll probably notice it when it's a problem if you ask pprof for a line-by-line profile).
sync.Mutex.Lock is implemented like this:
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if race.Enabled {
race.Acquire(unsafe.Pointer(m))
}
return
}
// Slow path (outlined so that the fast path can be inlined)
m.lockSlow()
}
lockSlow is, of course, a bit more complicated.
- gavinclarke0 5y agoUsing atomic instructions is still generally considered lockless with the idea that since the lock is at the instruction level and thus progress can still be guaranteed it is "lockless". If this is true in practice depends on a whole host of factors. I do not doubt majority of cases a mutex is faster/more appropriate. However the key there is looking at what m.lockslow() or unlockSlow() does which is called on lock contention. They become spinlocks that are not entirely dissimilar to my implementation. The idea is that a lockless algorithm is trading a worse best case i.e m.lockSlow is not called for a better worse case. Again if this is true in this exact case, I am not sure as I have not benchmarked it against a naive mutex based ringbuffer.