I have written a blocking queue in C very similar to the one provided by Java's BlockingQueue class. The queue basically provides the following API:
void blocking_queue_add(Blocking_Queue* bq, void* element);
void* blocking_queue_get(Blocking_Queue* bq);
and has the following properties:
- It has a fixed size.
- If
blocking_queue_addis called when the queue is full, the caller is blocked until the queue has space. - If
blocking_queue_getis called when the queue is empty, the caller is blocked until there is data to fetch. - If multiple callers are blocked waiting for
blocking_queue_addorblocking_queue_get, they need to be served in order (FIFO).
I'm having a hard time implementing a queue that satisfies the last point while keeping acceptable performance. I'm using pthreads to do the synchronization and, in order to satisfy the FIFO requirement, I am relying on pthread_cond_wait and pthread_cond_broadcast functions. The whole idea is that all blocked callers will be waiting on a condition. Once one of them can be served, a broadcast signal is emitted and they all wake up. At this point, they can easily check who is the selected one, based on an internal FIFO state. The others voluntarily block themselves by calling pthread_cond_wait again and wait their turn.
This implementation seems to work fine and the queue itself seems to be working. However, I have a huge performance drop when there are a lot more consumers than producers, or vice versa. The problem is that when I have this unbalance, there will always be a lot of blocked callers, so the overhead of waking up all of them by calling pthread_cond_broadcast everytime becomes unacceptable.
I'm looking for hints about how to solve this. I compared my queue with Java's implementation and it is faster when dealing with small workloads, but when I test a scenario with a large and totally unbalanced workload, my implementation is a disaster, and Java's implementation seems to be only linearly slower. In fact, if I remove all of the queue-related code from my implementation and just change both blocking_queue_add and blocking_queue_get functions to just have the cond/broadcast logic, it is already a disaster.
Any input is appreciated, thanks.
Brandon's idea improved the performance a lot. Performance now seems to be more than 2x better than the Java implementation.
For anyone interested in the future, I made the source-code open source: https://github.com/felipeek/c-fifo-blocking-queue