Multiple vs single condition variable
Asked Answered
L

1

6

I'm working on an application where I currently have only one condition variable and lots of wait statements with different conditions. Whenever one of the queues or some other state is changed I just call cv.notify_all(); and know that all threads waiting for this state change will be notified (and possibly other threads which are waiting for a different condition).

I'm wondering whether it makes sense to use separate condition variables for each queue or state. All my queues are usually separate, so a thread waiting for data in queue x doesn't care at about new data in queue y. So I won't have situations where I have to notify or wait for multiple condition variables (which is what most questions I've found are about).

Is there any downside, from a performance point of view, for having dozens of condition variables around? Of course the code may be more prone to errors because one has to notify the correct condition variable.

Edit: All condition variables are still going to use the same mutex.

Logical answered 29/5, 2018 at 9:18 Comment(8)
the issue with having multiple conditional variables is if/when you need to wait on multiple of them, there are no good ways of doing it, if you don't need to wait on multiple conditional variables it is a better to have multiple of them because there will be less useless notificationsWarrantor
Possible dublicate: #3652556Overcharge
@MikhailVasilyev All condition variables are going to use the same mutex, so I believe my question is different from the linked question.Logical
Makes sense if you want to notify only certain threads, only those associated with a particular particular cv will be notified.Inopportune
I have used a single condition variable to handle multiple waits with very acceptable results. I used an atomic int as an 'address' to identify the waits and notifies. Only waits whose 'address' matched the notify address would respond to the notify. I wrapped all the fussy stuff in a class, and the usage is basically foo.wait(my_address);, and foo.notify(some_address); Many different threads could be waiting, and I even handled the case where multiple threads are waiting on the same address, which was a common scenario in my application.Spectroscopy
I suggest putting the condition variable, mutex, and 'address' in a class, along with whatever other supporting data you need to get the functionality you are after. This way the class can be thoroughly tested, and all of the potential errors are contained in one place.Spectroscopy
@ttemple, re, "I have used a single condition variable to handle multiple waits with very acceptable results." Very acceptable for you. Different applications have different performance requirements. Somebody else's application might not be able to afford the overhead of many threads for which the notification was not intended all waking up, re-acquiring the mutex, checking for whether there really is work for them to do, and then going back to sleep when there isn't any.Logroll
@james large, Very true. All applications are different. As the OP pointed out, there is a tradeoff between performance and maintainability. In my case, the code was greatly simplified, and was much easier to reason about than using many condition variables. With all of his condition variables using the same mutex, I'm not sure if he benefits from the complexity of dozens of cv's.Spectroscopy
B
4

I would recommend having one condition_variable per actual condition. In producer/consumer, for example, there would be a cv for buffer empty and another cv for buffer full.

It doesn't make sense to notifiy all when you could just notify one. That makes me think that your one condition_variable is in reality managing several actual conditions.

You're asking about optimization. On one hand, we want to avoid premature optimization and do the profiling when it becomes a problem. On the other hand, we want to design code that's going to scale well algorithmically from the beginning.

From a speed performance point of view, it's hard to say without knowing the situation, but the potential for significant negative impact on speed from adding cv's is very low, completely dwarfed by the cost of a wait or notify. If adding more cv's can get you out of calling notify-all, the potential for positive impact on speed is high.

Notify-all makes for simple-to-understand code. However, it shouldn't be used when performance matters. When a thread waiting on cv is awoken, it is guaranteed to hold the mutex. If you notify-all, every thread waiting on that cv will be woken up and immediately try to grab the mutex. At that point, all but one will go back to sleep (this time waiting for the mutex to be free). When the lucky thread releases the mutex, the next thread will get it and the next thing it will do is check the cv predicate. Depending on the condition, it could be very likely that the predicate evaluates to false (because the first thread already took care of it, or because you have multiple actual conditions for the one cv) and the thread immediately goes back to sleep waiting on the cv. One-by-one, every thread now waiting on the mutex will wake up, check the predicate, and probably go back to sleep on the cv. It's likely that only one actually ends up doing something.

This is horrible performance because mutex lock, mutex unlock, cv wait, and cv signal are all relatively expensive.

Even when you call notify-one, you might consider doing it after the critical section to try to prevent the one waking thread from blocking on the mutex. And if you actually want to notify-all, you can simply call notify-one and have each thread notify-one once it finishes the critical section. This creates a nice linear cascade of turn-taking versus an explosion of contention.

Barnaby answered 31/5, 2018 at 3:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.