dev@grizzly.java.net

Re: Async HTTP responses

From: Oleksiy Stashok <Oleksiy.Stashok_at_Sun.COM>
Date: Fri, 28 Nov 2008 12:09:16 +0100

>>>>>>>>
>>>>>>>> * Output buffer.
>>>>>>>> @@ -54,16 +65,60 @@
>>>>>>>> * * @author Jean-Francois Arcand
>>>>>>>> * @author Scott Oaks
>>>>>>>> + * @author Alexey Stashok
>>>>>>>> */
>>>>>>>> -public class SocketChannelOutputBuffer extends
>>>>>>>> InternalOutputBuffer{
>>>>>>>> +public class SocketChannelOutputBuffer extends
>>>>>>>> InternalOutputBuffer {
>>>>>>>> + private static Logger logger = SelectorThread.logger();
>>>>>>>> + private static final int DEFAULT_BUFFER_POOL_SIZE = 16384;
>>>>>>>> +
>>>>>>>> + private static int maxBufferPoolSize =
>>>>>>>> DEFAULT_BUFFER_POOL_SIZE;
>>>>>>>> +
>>>>>>>> /**
>>>>>>>> + * ByteBuffer pool to be used with async write
>>>>>>>> + */
>>>>>>>> + private static Queue<ByteBuffer> bufferPool =
>>>>>>>> + new
>>>>>>>> ArrayBlockingQueue<ByteBuffer>(maxBufferPoolSize);
>>>>>>>
>>>>>>> Can you instead have a pool per instance, and limit the size
>>>>>>> of the pool based on the number of maxThreads? Take a look at
>>>>>>> AIO
>>>>>>>
>>>>>>> http-aio/src/main/java/com/sun/grizzly/aio/http/
>>>>>>> AsyncSocketChannelOutputBuffer.java
>>>>>>>
>>>>>>> there I avoid synchronized on a single queue.
>>>> Per instance - yes we can. It will probably improve the perf, but
>>>> increase memory consumption, so it's trade off here.
>>>
>>> Why would it? The idea is to have:
>>>
>>> size per instance * instance_number == overall size
>>>
>>> Right now you have overall_size shared amongst all instance.
>> But we don't create the ByteBuffers at startup. We add them by
>> demand. And shared pool will grow slower than pool per instance. So
>> memory consumption will be less.
>
> Hum ;-) I'm not sure I agree :-) Eventually you will reach the max
> memory anyway.
That's right. What I'm saying, is that pool per instance is less
economic than global one and it will reach the maximum much faster
than global pool.

> Plus you need to sync on the 'global' cache ;-) All threads will
> eventually dead ends there, trying to get one ByteBuffer.
Well they will not dead. But for sure synchronization cost should be
paid.

One more thing is that ProcessorTask count is not directly dependent
on the number of worker threads. The processor task pool represents
unbounded linked queue. In this case It means we can not control the
real amount of pooled buffers.

Basically I agree with you, that pool per instance will perform
faster, because less threads will be synchronized on it, but the idea
looks dangerous for me.

We can use concurrent linked queue as 'global' ByteBuffer cache,
instead of BlockingQueue. ConcurrentLinkedQueue is not synchronized
(thread safe though). And we can control the size of this pool with
weak (non synchronized) checking, which means the limits will not be
strictly honored. And pool could get larger than max-size. Actually
the real limit for this queue with weak checking will be max-size +
worker-threads-number.

What do you think?

Thanks.

WBR,
Alexey.

> At least this is what I've measured with AIO ;-)
>
> :-)
>
> -- Jeanfrancois
>
>
>>>> As for limit size, based on max threads number - not sure it's
>>>> right solution. These values are completely independent, or I
>>>> missed something?
>>>>>>>> + }
>>>>>>>> +
>>>>>>>> + + /**
>>>>>>>> + * Sets the underlying selection key of the output
>>>>>>>> channel.
>>>>>>>> + * @param selectionKey the underlying selection key of
>>>>>>>> the output channel.
>>>>>>>> + */
>>>>>>>> + public void setSelectionKey(SelectionKey selectionKey) {
>>>>>>>> + this.selectionKey = selectionKey;
>>>>>>>> + channel = selectionKey.channel();
>>>>>>>> + }
>>>>>>>
>>>>>>> setChannel() is always invoked by DefaultProcessorTask
>>>> Now it invokes setSelectionKey(), cause we need it for async write.
>>>>>>>>
>>>>>>>> }
>>>>>>>> @@ -278,4 +507,20 @@
>>>>>>>> public static void setMaxBufferedBytes(int
>>>>>>>> aMaxBufferedBytes) {
>>>>>>>> maxBufferedBytes = aMaxBufferedBytes;
>>>>>>>> }
>>>>>>>> +
>>>>>>>> +
>>>>>>>> + public static void setMaxBufferPoolSize(int size) {
>>>>>>>> + int poolSize = (size >= 0) ? size :
>>>>>>>> DEFAULT_BUFFER_POOL_SIZE;
>>>>>>>> +
>>>>>>>> + if (maxBufferPoolSize == poolSize) return;
>>>>>>>> + + maxBufferPoolSize = poolSize;
>>>>>>>> +
>>>>>>>> + bufferPool = new
>>>>>>>> ArrayBlockingQueue<ByteBuffer>(maxBufferPoolSize);
>>>>>>>> }
>>>>>>>> +
>>>>>>>
>>>>>>> Hum I don't like set method that does works :-) Can we change
>>>>>>> the name to ajustMaxBufferPoolSize()?
>>>> Hmm, and what is the difference? :))
>>>
>>> just working. Usually when invoking setXXX, you just the value
>>> without logic. Just cosmetics
>> ok.
>> Thanks.
>> WBR,
>> Alexey.
>>>
>>>
>>> A+
>>>
>>> -- Jeanfrancois
>>>
>>>> ---------------------------------------------------------------------
>>>> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
>>>> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
>>> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>>>
>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
>> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe_at_grizzly.dev.java.net
> For additional commands, e-mail: dev-help_at_grizzly.dev.java.net
>