News for hackers. Stuff that matters.
Re:What every systems programmer should know about lockless ... by jeff571 on Thursday November 02, 2017 @02:00AM (#15608171)
Keep in mind lockless algorithms are not necessarily more scalable than lock-based algorithms, usually have higher constant overheads, and are significantly easier to get wrong.

However, this post is, in part, about how to implement locks.

Re:What every systems programmer should know about lockless ... by taneq on Thursday November 02, 2017 @02:41AM (#15608316)
> However, this post is, in part, about how to implement locks.

I haven't read the article in-depth but yeah, it seems like it maybe would have been better titled "What every C++ systems programmer should know about std::atomic". Which is still cool but from the title I was expecting some fancy voodoo algorithms for threadsafe non-locking FIFO queues or something.

Re:What every systems programmer should know about lockless ... by banachtarski on Thursday November 02, 2017 @04:21AM (#15608678)
I think it's meant to be pretty fundamental. It's not supposed to include "voodoo algorithms" because it literally says "what every systems programmer should know...". The voodoo algorithm is a voodoo algorithm because not everyone needs to know it. In contrast, a mutex and all of that stuff is a consequence of how memory sequencing on a CPU works. If you understand those concepts, the rest is a hop, skip, and a jump away.
Re:What every systems programmer should know about lockless ... by taneq on Thursday November 02, 2017 @05:23AM (#15608911)
I guess my beef was mostly in response to the word "lockless", which I'd hoped meant "not using any wait-for-other-thread type semantics" but in this context seems to mean "implementing locking without using OS-provided locking primitives, although standard library is fine."

I was hoping that there was some cool new trick I'd missed or fundamentally different way to do concurrency than locking. I get disappointed easily in cases like this. :/

Re:What every systems programmer should know about lockless ... by Animats on Thursday November 02, 2017 @03:02PM (#15613265)
Me too. If you can do lockless add-to-queue and remove-from-queue, that's valuable. It's hard to get that right, especially on non x86/amd64 platforms. Every network packet, and often every interrupt, goes through a queue operation like that. Not stalling the other CPUs on each interrupt is important.
Re:What every systems programmer should know about lockless ... by johndubchak on Thursday November 02, 2017 @01:41PM (#15612489)
I think that might be what parts of this upcoming release might contain:

https://www.amazon.com/C-Concurrency-Action-Anthony-Williams...

Re:What every systems programmer should know about lockless ... by buserror on Thursday November 02, 2017 @04:13AM (#15608657)
Completely agree with this, the topic is much wider than the meat of the paper. A bit of a clickbait here.
Re:What every systems programmer should know about lockless ... by banachtarski on Thursday November 02, 2017 @04:19AM (#15608674)
I think it's worth thinking in terms of the probability of contention per cycle. If the odds of contention is very low, lock-free will be much better (imagine accessing a shared data structure which is vast compared to the range of usage in a typical use).

Also, I think it's worth noting that it's really not that much more complicated than a simple mutex if you use the default sequential consistency memory order on an atomic. When you start attempting to optimize around certain patterns using the weaker memory orders, yea, things get a lot tougher.

Re:What every systems programmer should know about lockless ... by yason on Thursday November 02, 2017 @04:42AM (#15608750)
If the odds for contention are low then something like a futex is pretty snappy, too.

Lockless solutions do naturally fit in certain cases. For example, producer-consumer queues where there's only some invariants that need to be respected. There the producer must not overwrite what the consumer hasn't consumed yet and the consumer must not advance to areas that the producer hasn't yet filled. Using a lockless approach both the producer and the consumer can continue to operate as long as they won't step on each others' toes. Instead, using a mutex or read-write locking would basically make the access to the queue mutually exclusive to the producer and consumer.

I, myself, like the approach of building my data out of near-immutable blocks and just replacing the references using atomic compare-and-swap, for example.

Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @06:27AM (#15609156)
There are two-lock queues that allow consumers to mostly run independently from the producers while still using bog-standard locks.

Still you are right that lock-free algos really shine in implementing message queues, but mostly because they can be implemented with fewer or cheaper memory operations, so even in the uncontended case, the lock-free queue can shave some cycles.

> I, myself, like the approach of building my data out of near-immutable blocks and just replacing the references using atomic compare-and-swap, for example.

Yes, one huge advantage of lock free algorithms is that you can have read mostly data structures with no contentions between readers (most rw-lock implementations do require readers to write on some shared state).

Re:What every systems programmer should know about lockless ... by banachtarski on Thursday November 02, 2017 @06:03AM (#15609054)
Yup using a layer of indirection definitely works well. Regarding the futex, I definitely agree that most futex implementations on most hardware are designed to spin and resolve quickly in low contention scenarios, but this won't be true on all consoles/hardware/OS configurations. For certain cases (like a game engine), I'd be less inclined to rely on the futex to do the right thing.
Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @06:33AM (#15609171)
Futex are a linux kernel-level specific waiting primitive [1]. The point of the OP is that mutex implemented on top of a futex has a purely userspace fast path that needs only a single CAS for each acquire and release in the uncontended space, so in this scenario an userspace spin-lock is not often significantly better (a spin lock release only need a store on some machines though). Spin locks are significantly worse in the contended space though.

[1] futexes enable Fast Userspace muTEXES, but futex themselves are kernel space and not particularly fast.

Re:What every systems programmer should know about lockless ... by chriscappuccio on Thursday November 02, 2017 @12:57PM (#15612123)
OpenBSD implements futex now, too. Major league userland responsiveness improvements for futex based mutexes.
Re:What every systems programmer should know about lockless ... by scott_s on Thursday November 02, 2017 @11:05AM (#15611046)
I think it's usually the other way around: when the odds of contention is very low, locks are better. Uncontended lock acquisition is relatively cheap, and it can be done once. The downside of using a lock is that it prevents other threads which are contending for that lock from proceeding, hence why locks are better when contention is low.

Lock free algorithms are generally better when the odds of contention are high, as you can devise algorithms that allow all threads to make progress at the same time. In general, this allows more parallelism, but the cost is that it typically requires more overall atomic operations.

Re:What every systems programmer should know about lockless ... by banachtarski on Thursday November 02, 2017 @01:49PM (#15612566)
> Lock free algorithms are generally better when the odds of contention are high, as you can devise algorithms that allow all threads to make progress at the same time. In general, this allows more parallelism, but the cost is that it typically requires more overall atomic operations.

I can't say I agree here. It really depends on the cost of the swap operation required to make this work. Consider a lock-free map for example. Under high contention, I'd prefer to wait for a lock than copy the map while spinning on an atomic. What would push the needle for me one way or another (with respect to locking vs lockless) is a blend between the probability of contention, the cost of the loop itself, and the cost of the swap.

Re:What every systems programmer should know about lockless ... by scott_s on Thursday November 02, 2017 @01:58PM (#15612644)
I don't know what you mean here: "than copy the map while spinning on an atomic." Why would we copy the map? And by "spinning on an atomic", do you mean just spin-waiting on some flag? That's, morally, a lock. A well designed lock free algorithm allows all threads to make progress at the same time, and if there is contention, an alternative that hopefully is not just redoing that same work. The cost is more atomic operations, which means that if there is low contention, you're paying more overhead than needed.
Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @11:24AM (#15611236)
It depends, on high contention, waiting writer threads will only touch a single shared cache line containing the lock (all the time if spinning, once if there is a proper thread queue). In a lock free algo, the writers might be stepping on each other toes all the time, with heavy cache-line pingpoing, which is not good for general throughput.

Lock free algos are very good when you have many pure readers and few or occasional writers.

Re:What every systems programmer should know about lockless ... by scott_s on Thursday November 02, 2017 @01:35PM (#15612417)
In my experience, once you get up to the 100s of threads potentially touching global structures, locks impeded scalability significantly. Even if each of those threads is modifying the global data. The key is to design your algorithms around the kind of structures that are amenable to lock free algorithms. (The experience I'm referring to: http://www.scott-a-s.com/files/pldi2017_lf_elastic_schedulin...)
Re:What every systems programmer should know about lockless ... by banachtarski on Thursday November 02, 2017 @01:47PM (#15612544)
Is a program that has 100s of threads realistic?
Re:What every systems programmer should know about lockless ... by dragontamer on Thursday November 02, 2017 @03:19PM (#15613404)
The Vega64 GPU that AMD just released has 4096 "threads" operating at one time (actually in flight), with up to 40,960 of them "at the ready" at the hardware level (kinda like Hyperthreading, except GPUs keep up to ~10 threads per "shader core" in memory for quick swap in-and-outs). Subject to memory requirements of course. A program that uses a ton of vGPR Registers on the AMD system may "only" accomplish 4096 threads at a time, and maybe only 5 per core (aka 20,480) of them are needed for maximum occupancy.

Its a weird architecture because of how each "thread" shares an instruction pointer (ie: NVidia has 32 threads per wavefront, AMD has 64 workitems per Work Group), so its not "really" the same kind of "thread" as in Linux pthreads. But still, the scope of parallelism on a $500 GPU today is rather outstanding.

All of these threads could potentially hit the same global memory at the same time. I mean, if you want bad performance of course, but its entirely possible since the global memory space is shared between all compute units in a GPU.

Re:What every systems programmer should know about lockless ... by scott_s on Thursday November 02, 2017 @01:52PM (#15612585)
Yes. See the paper I linked to. I work on a parallel and distributed dataflow system called IBM Streams. When we have thousands of our operators running on a large system, it is realistic for there to be 100s of threads.
Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @02:04PM (#15612704)
there are fairly mainstream machines with more than 100s of hardware threads.
Re:What every systems programmer should know about lockless ... by jeff571 on Thursday November 02, 2017 @09:53AM (#15610372)
The lock could still be faster. atomic ops are more expensive than regular opcodes even when there is zero contention. On x86, a lock requires only a single atomic opcode and can be released with a normal write (because of TSO). Since it's uncontended it won't spin at all. Therefore, any alternative lock-free algorithm that uses more than a single atomic op is actually more expensive.
Re:What every systems programmer should know about lockless ... by pizlonator on Thursday November 02, 2017 @12:54PM (#15612090)
Lock-less algorithms sometimes have more CAS instructions on the critical path, so may be slower under zero contention.

I think you’re right in many cases though.

Re:What every systems programmer should know about lockless ... by sdenton4 on Thursday November 02, 2017 @06:23AM (#15609140)
As the text puts it: "And as always, consider the tradeoffs you make between complexity and performance. Lockless programming is a perilous activity with a proud tradition of code that is ever so subtly wrong."
Re:What every systems programmer should know about lockless ... by saas_co_de on Thursday November 02, 2017 @08:20AM (#15609679)
Lock-free using CAS type operations looks good in theory but having to do a cache flush for every atomic operation really bogs things down when you get into complex algorithms so the simple lock ends up being cheaper.

An alternative lock-free approach is to segmented memory between threads and then coordinate between threads using messages passed with ring buffers. This can be done entirely without atomic operations but hits other scaling problems and is also extremely difficult to implement so probably not widely applicable.

Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @11:18AM (#15611172)
CAS don't do cache flushes. Caches are fully coherent so they never enter into the window in any lock-free algorithms.

Also any lock implementations also use a cas or equivalent internally (you could use the Dekker algorithm, but it is not cheper).

Re:What every systems programmer should know about lockless ... by dragontamer on Thursday November 02, 2017 @01:32PM (#15612402)
> Also any lock implementations also use a cas or equivalent internally

But if you have a data-structure with say, 20 ints... you have two approaches.

1. Use a mutex (which may be CAS-based under the hood) at the beginning and end: 1 lock and 1 unlock. The 20-ints are then accessed with non-atomic operations.

2. Use atomic operations on those 20-ints. But in this case, you pay the "atomics cost" each time.

In many cases, #1 may be faster than #2.

-----

Sorry for the 2nd response, but this is a different idea than what I was talking in my 1st response.

Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @01:49PM (#15612564)
Using 20 cas to update 20 ints it is probably not the best idea. There are significantly more than two approaches (seqlocks, RCU, persistent data structures, etc). Which one works best depends on the actual use case.
Re:What every systems programmer should know about lockless ... by saas_co_de on Thursday November 02, 2017 @12:47PM (#15612032)
> CAS don't do cache flushes. Caches are fully coherent

Not in the sense of flushing from cache to memory, but yes in the sense of data from a local core cache needing to be sent to another core.

Caches may be coherent but coherency is definitely not free, especially on multi-processor systems, which you probably care about if you are writing this kind of code.

Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @01:14PM (#15612267)
CAS has no effect on the cache (other than guaranteeing that the cacheline to be in the exclusive state for the time to perform the load+op+store). CASs and other atomic RMW ops in general, in all common architectures are purely local operations that do not affect the coherency fabric (not any more than a store would, memory barriers do not even do that).

The only thing that can be said to be 'flushed' by a CAS (at least on a x86 where it has full barrier semantic) is the store buffer (which is not a cache), but even there the only thing it does is simply prevent more recent loads [1] to complete before any store older or equal than the cas has completed. The store buffer is not really flushed and is still drained at the same speed it would without at cas.

[1] or any other operation for simplicity of implementation.

Re:What every systems programmer should know about lockless ... by dragontamer on Thursday November 02, 2017 @12:46PM (#15612020)
> CAS don't do cache flushes. Caches are fully coherent so they never enter into the window in any lock-free algorithms.

Perhaps "Cache Flush" is the wrong word, but as long as cores are forced to communicate, you're going to get a performance penalty.

I know when you do a simple atomic_increment on x86_64 platforms, things can be 2x slower at a minimum than a non-atomic increment.

"Cache Coherence" simply means that the CPU handles all of those edge cases for you. But it still needs to be handled, and that entails a performance penalty.

If you have a 4-core / 8-thread machine (Like a recent Intel i7), its faster to have 8-Non Atomic counters that are added up at the end... rather than to have 1-atomic counter with 8-threads doing atomic-increment on it.

----------------

Any core that modifies a memory location invalidates the cache for all other cores. (ie: Core 1 does atomic_inc(x), implies that Core2/Core3/Core4 all have an invalid value of "x". So atomic_inc(x) causes a cache-flush on Core2/Core3/Core4)

Again, I'm not an expert, but that's my understanding of how modern systems work.

-------

I looked up the Intel Architecture Manual, and they have the following table:

https://imgur.com/uesh8co

If the data is in another core's L1 cache, the latency increases dramatically, especially on a "Dirty Hit". (ie: Core1 is trying to read "x", but Core3 is telling the other cores that "x" has changed)

In effect, its more efficient for an Intel CPU to access something from L3 cache than for it to access a "Dirty" value from another core. Heck, based on these numbers, even a "Clean" hit is more costly than using the L3 cache!

Re:What every systems programmer should know about lockless ... by arielweisberg on Thursday November 02, 2017 @01:02PM (#15612163)
You pay the sharing cost regardless of whether the operation used to access the cache line is atomic or guarantees visibility. At least on x86. See false sharing.

The extra penalty introduced by common atomic operations that make strong guarantees about ordering is waiting for the store buffer to flush all prior stores before the result of the atomic operation is flushed out of the store buffers to L1 and then the atomic operation is flushed.

I hesitate to make generalizations about memory visibility requirements and costs as it is very API and hardware specific. Weaker guarantees exist and would have different costs.

TL;DR The total memory overhead is the same the difference is the atomic operation stalls the CPU that is performing the atomic operation unless it can speculate past it.

Also blind stores can be resolved in the background and don't block the CPU from continuing as long as the cache line is never read from.

Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @01:19PM (#15612311)
Exactly.

Understanding how atomic instructions work and their effect (or lack of) on the system is important for optimizations.

Re:What every systems programmer should know about lockless ... by dragontamer on Thursday November 02, 2017 @01:29PM (#15612381)
I mean, that's why I'm participating in the discussion. :-)

Ultimately, I'm basing my viewpoint from this PDF: the "Is parallel programming Hard" free online book.

https://www.kernel.org/pub/linux/kernel/people/paulmck/perfb...

> Line 1 defines an atomic variable, line 5 atomically increments it, and line 10 reads it out. Because this is atomic, it keeps perfect count. However, it is slower: on a Intel Core Duo laptop, it is about six times slower than non-atomic increment when a single thread is incrementing, and more than ten times slower if two threads are incrementing.

The PDF changes over time. But this line can be found on page 42 at the moment.

--------

So according to the tests done by the author here, atomic operations are CERTAINLY slower, even in single-threaded cases.

I can't say I understand why they're slower, but something is definitely going on.

Re:What every systems programmer should know about lockless ... by gpderetta on Thursday November 02, 2017 @02:00PM (#15612669)
Of course they are slow, atomic RMW [1] at least on x86 stall the pipeline waiting for the implied memory barrier to be flushed out of the store buffer. What me and arielweisberg have been trying to say is that barriers and RMW are purely local and have nothing to do with caches [1], as such they are purely a constant overhead on algorithms and they scale perfectly: in the contended case the scaling cost is completely determined by the number of cacheline written to and the number of cores involved and it is completely independent of the number of atomic operations performed per operation.

[1] atomic stores and loads are extremely cheap on x86.

[2] you can see that from the cost: a RMW or membar is around 20-30 clock cycles, while cross core communication costs in the order of 100s cycles.

edit: s/buffer/barrier/

Re:What every systems programmer should know about lockless ... by dragontamer on Thursday November 02, 2017 @02:03PM (#15612698)
Thanks for the explanation. I think I get it now.
Re:What every systems programmer should know about lockless ... by chrisseaton on Thursday November 02, 2017 @12:01PM (#15611567)
> having to do a cache flush for every atomic operation

I don't think it's as bad as that - I've heard it's only as expensive as hitting the L2 cache.