3 ms·
I will personally recommend that unless you are writing performance sensitive code*, don’t use mutexes at all because they are too low-level an abstraction. Use
by kccqzy 11mo ago
I will personally recommend that unless you are writing performance sensitive code*, don’t use mutexes at all because they are too low-level an abstraction. Use MPSC queues for example, or something like RCU. I find these abstractions much more developer friendly.
*: You may be, since you are using Rust.
- jltsiren 11mo agoA mutex is a natural abstraction when there is exactly one of them. You have a bunch of tasks doing their own stuff, with shared mutable state behind the mutex. When you start thinking about using two mutexes, other abstractions often become more convenient.
- fpoling 11mo agoI have found out that mutex solutions are more maintainable and amendable without big redesigns compared with channels or RCU. Consider a simple case of single producer-single consumer. While one can use bounded channels to implement back-pressure, in practice when one wants to either drop messages or apply back-pressure based on message priority any solution involving channels will lead to pile of complex multi-channel solutions and select. With mutex the change will be a straightforward replace of a queue by a priority queue and an extra if inside the mutex.
- kccqzy 11mo agoA channel can be backed by a priority queue if you wish. It’s just an abstraction. The channel internally probably uses mutexes too; it’s just that it’s helpful not to see mutexes in application code.
- fpoling 11mo agoSurely one can abstract priority queue with mutexes into own data structure. However it will contain enough application-specific logic so a chance of reuse will be slim. So by directly working with mutexes one will have simpler code overall that can still be more easy to adapt to changing requirements. In general the problem with channels is that they are not flexible enough while being rather abstract. A better abstraction is message passing with one message queue per thread like in Erlang. IMO it can cover more cases before one needs mutexes. But even with that proper back pressure and rate limiting is hard.
- VorpalWay 11mo agoChannels and RCU can often be better for performance as well. I have run into scaling issues due to contention way too many times. Sometimes just because two things were in different parts of the same cache line. Sharing as little mutable state as possible is often the best way to scale to a large number of CPU cores. RCU can help with that if you have data that is mostly read but rarely written. If your workload is balanced or even write heavy RCU is probably not a good option. I have even had to redesign because I had too much contention on plain old atomic reference counters, almost 30 % of my runtime was reference counting of a small number of specific Arcs, and hardware performance counters pointed at cache line contention. I redesigned that code to avoid Arcs entirely which also allowed some additional optimisations, resulting in cutting approximately 40 % of my runtime in total. So, each specific use case should be approached individually if you care about performance. And always profile and benchmark. If the code isn't performance critical, by all means do what you think is most maintainable. But measure, because you are probably wrong about what part of your code is the bottleneck unless you measure.
- pdimitar 11mo agoVery interesting, I want to learn more. How do you diagnose contention on atomic counters? And how do you diagnose cache line contention? And how do you rewrite that code away from Arc-s?