You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mina.apache.org by "Trustin Lee (JIRA)" <ji...@apache.org> on 2007/01/22 07:08:30 UTC

[jira] Resolved: (DIRMINA-328) org.apache.mina.filter.codec.CumulativeProtocolDecoder uses an inefficient accumulation strategy

     [ https://issues.apache.org/jira/browse/DIRMINA-328?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]

Trustin Lee resolved DIRMINA-328.
---------------------------------

       Resolution: Fixed
    Fix Version/s: 1.0.2
         Assignee: Trustin Lee

Thank you for the patch!  It has been applied to all branches.

> org.apache.mina.filter.codec.CumulativeProtocolDecoder uses an inefficient accumulation strategy
> ------------------------------------------------------------------------------------------------
>
>                 Key: DIRMINA-328
>                 URL: https://issues.apache.org/jira/browse/DIRMINA-328
>             Project: MINA
>          Issue Type: Improvement
>          Components: Filter
>    Affects Versions: 1.0.2, 1.1.0, 2.0.0-M1
>         Environment: I have only studied the implementation thus far, and environment isn't a factor in this analysis.
>            Reporter: Steven E. Harris
>         Assigned To: Trustin Lee
>            Priority: Minor
>             Fix For: 1.0.2
>
>         Attachments: CumulativeProtocolDecoder.java.patch
>
>
> I've just started writing a MINA protocol decoder extending the class
> org.apache.mina.filter.codec.CumulativeProtocolDecoder, and I noticed
> some potential pathological behavior with its current implementation
> of its storeRemainingInSession() method:
> ,----
> | private void storeRemainingInSession(ByteBuffer buf, IoSession session)
> | {
> |     ByteBuffer remainingBuf = ByteBuffer.allocate( buf.remaining() );
> |     remainingBuf.setAutoExpand( true );
> |     remainingBuf.put( buf );
> |     session.setAttribute( BUFFER, remainingBuf );
> | }  
> `----
> The ByteBuffer passed as the first argument is either the buffer
> supplied to the decode() method, or it's buffer stored in the
> IoSession by a previous call to decode(), allocated and owned by
> CumulativeProtocolDecoder.
> The current implementation unconditionally allocates a fresh buffer
> and copies any unconsumed data from the current buffer in use to the
> new buffer, then stores this new buffer for use on a subsequent call
> to decode().
> Consider two abberrant cases: when the doDecode() method consumes only
> one byte and returns false, and when it consumes all but one byte and
> returns false. Assume the buffer to be decoded had N bytes
> remaining.
> In the first case, a new buffer gets allocated of size N - 1, with N -
> 1 bytes copied, and the current buffer is abandoned. That's a lot of
> spurious allocation with a potential reduction in buffer capacity for
> the next pass.
> In the second case, a new buffer gets allocated of size 1, with 1 byte
> copied, and the current buffer is abandoned. We now have a capacity of
> only 1 in the new buffer, having tossed away the larger capacity of
> the previous buffer. This new buffer will certainly have to grow to
> accommodate any new content, causing even more reallocation.
> I suggest we take advantage of the ByteBuffer.compact() method to
> attempt to preserve capacity and avoid reallocation when possible. Of
> course, the same amount of bytes must be copied with either approach.
> My proposed implementations assume that we're not allowed to hang on
> to the buffer supplied to our ProtocolDecoder.decode() call. First,
> here's one that checks the session attribute again to see if we're
> using a buffer we've previously allocated ourselves:
> ,----
> | private void storeRemainingInSession(ByteBuffer buf, IoSession session)
> | {
> |     ByteBuffer sessionBuf = ( ByteBuffer ) session.getAttribute( BUFFER );
> |     if ( sessionBuf != null )
> |     {
> |         // We're reusing our own buffer.
> |         assert( buf == sessionBuf );
> |         buf.compact();
> |     }
> |     else
> |     {
> |         // We're using a supplied buffer, so we need to copy it.
> |         final ByteBuffer remainingBuf = ByteBuffer.allocate( buf.capacity() );
> |         remainingBuf.setAutoExpand( true );
> |         remainingBuf.put( buf );
> |         session.setAttribute( BUFFER, remainingBuf );
> |     }
> | }
> `----
> Alternately, we could keep a boolean flag around from the beginning of
> the decode() method the first time we check the session attribute, and
> only call storeRemainingInSession() if we need to copy a buffer we
> didn't allocate:
> ,----
> | public void decode( IoSession session, ByteBuffer in,
> |                     ProtocolDecoderOutput out ) throws Exception
> | {
> |     boolean usingSessionBuffer = true;
> |     ByteBuffer buf = ( ByteBuffer ) session.getAttribute( BUFFER );
> |     // if we have a session buffer, append data to that otherwise
> |     // use the buffer read from the network directly
> |     if( buf != null )
> |     {
> |         buf.put( in );
> |         buf.flip();
> |     }
> |     else
> |     {
> |         usingSessionBuffer = false;
> |         buf = in;
> |     }
> `----
> ...
> ,----
> |     if ( buf.hasRemaining() )
> |     {
> |         if ( usingSessionBuffer )
> |             buf.compact();
> |         else
> |             storeRemainingInSession( buf, session );
> |     }
> |     else
> |     {
> |         if ( usingSessionBuffer )
> |             session.removeAttribute( BUFFER );
> |     }
> `----
> I think the second approach is better. Find a patch attached.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: https://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira