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

David Holmes dcholmes at optusnet.com.au
Mon Aug 11 20:50:40 EDT 2008


Dan,

Your second case isn't a continuous take() because it does:

ByteBuffer bytes = encoder.encode(chars);

that might take sufficient time for the producer to produce the next item
(or two) with no contention.

That's why I said that I would expect:

for(;;) queue.take();

to also cause maximum contention.

David

> -----Original Message-----
> From: Daniel Harvey [mailto:dharvey at tachyoncm.com]
> Sent: Tuesday, 12 August 2008 10:48 AM
> To: dholmes at ieee.org
> Cc: concurrency-interest at cs.oswego.edu
> Subject: Re: [concurrency-interest] Single producer,single consumer:
> unexpected delays for producer
>
>
> 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.
>
> -Dan
>
> 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