[concurrency-interest] Single producer, single consumer: unexpected delays for producer

Daniel Harvey dharvey at tachyoncm.com
Mon Aug 11 20:47:38 EDT 2008

I'm not sure whether the queue size is non-zero in the fast case,  
though I will test this tomorrow and report back. But something  
doesn't make sense here because in the second case -  the continuous  
take() one - there should be an even greater likelihood of  
encountering a non-empty queue... I really feel like the issue has to  
be the socket write interfering with the thread-signaling of the  
existence of a new message to process: I'm fairly sure that I could  
add other time-taking calculations to the consumer thread (in place  
of the socket.write() ) and they wouldn't slow down the producer.  
I'll add something like this tomorrow and see whether my assessment  
is accurate.

The following isn't a very elegant solution, and it might hammer the  
CPU too much, but I have experimented with having the consumer do a  
periodic sending of a batch of messages, then sleeping and checking  
for new messages every few ms (rather than waiting), and repeating.  
With this approach there's no signal between threads and I think the  
producer operates much faster (even with the necessary  
synchronization). Of course I am adding latency, but if throughput is  
the bottom line, it might be a useable solution.


On Aug 11, 2008, at 8:29 PM, David Holmes wrote:

> Dan,
>> 3) I put some code in to keep track of how many messages are
>> typically in the queue when the consumer thread finishes it's take(),
>> and it is typically 0... ie the majority (around 80-90%) of the time
>> a single message is being removed from the queue.
> Okay so that explains the contention. The producer is expending effort
> acquiring two locks and waking up the producer.
>> that's still a big difference... specifically:
>> for (;;) {
>> 	CharBuffer chars = CharBuffer.wrap(queue.take());
>> 	ByteBuffer bytes = encoder.encode(chars);
>> 	socketChannel.write(bytes);
>> }
>> causes queue.offer(message) to take 3 times longer to execute than if
>> the consumer executes:
>> for (;;) {
>> 	CharBuffer chars = CharBuffer.wrap(queue.take());
>> 	ByteBuffer bytes = encoder.encode(chars);
>> }
> Can I assume that in the fast case the queue size is not normally  
> zero? If
> so my theory here is that when the consumer uses the socketChannel  
> it is
> introducing additional contention with the prodcuer that keeps the  
> producer
> and conumer effectively in lock-step:
> - consumer blocks on take()
> - producer gets item and does offer() and has to wakeup consumer
> - consumer processes item and uses resources needed by producer
> - producer is blocked or delayed getting next item
> - consumer calls take() again and blocks
> - producer produces and calls offer() and has to wakeup consumer
> - repeat
> However in that case I would expect:
>   for(;;) queue.take();
> to also impose maximum contention with the producer.
> David

More information about the Concurrency-interest mailing list