3 ms·
So for a single critical section per thread, there's a small fixed overhead for every scheduling event in the system (the changes to the scheduler to check for
by addaon 5y ago
So for a single critical section per thread, there's a small fixed overhead for every scheduling event in the system (the changes to the scheduler to check for whether the IP is in the critical section), and there's a restart cost if rescheduling actually happens during the critical section, but there's no additional code size or instructions on the critical path itself. But once managing multiple critical sections per thread this is no longer true...
Seems like interesting tradeoffs with an approach where the kernel manages a scheduling generation number in shared memory with each thread that gets incremented each scheduling, and having the user code responsible for checking at the end of the critical section whether it matches the value at the beginning. Probably an instruction or three less (and less pointer chasing) per scheduling event, but it eats a register during the critical section and grows the critical section by a few instructions, which also (for very tight critical sections) increases the chances they need a restart...
- PaulDavisThe1st 5y agoThe LWN article mentions that compare-and-exchange is considered "slow", and the design you're mentioning here, you'd need that. This is an alternative to cmpxchg-style solutions, which you can implement entirely in user space with no kernel collarboration. As I mentioned in another thread, the restartable sequence approach seems very limited in applicability, since it only offers you something in cases where it is specifically preemption, not multithreaded access to the data, that you need to protect against.
- addaon 5y agoEdit: Oops, leaving this for posterity, but there's an error below. At the point marked OOPS cmpxchg or another atomic op is the (only?) way to avoid a race condition when there's a potential interrupt between the write-back instruction of the atomic operation and the comparison to the generation. My bad. -- For this use case, I don't think the approach I mentioned requires an atomic cmpxchg. Fast path (no pre-emption): - Read generation number shared memory location (GNSML) to register - Perform atomic operation - Compare (non-atomic) *GNSML to register [OOPS] - Equal, so continue Slow path (pre-emption): - Read generation number shared memory location (GNSML) to register - Perform (part of) atomic operation -- Pre-emption occurs -- kernel updates GNSML, already has plenty of memory barriers with mode transitions - Perform (rest of) atomic operation - Compare (non-atomic) *GNSML to register -- because we're reading on the far side of the barrier, see new value - Different, so jump to restart