You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@directory.apache.org by Emmanuel Lécharny <el...@gmail.com> on 2018/09/29 04:04:54 UTC
ASN.1 encoding experiment
Hi guys,
I had some time last evening and during the following insomnia to do
some tests and bechnmark using the reverse order encoding of ASN.1 PDU
(the PDU is encoded starting from the end, which allows the encoding of
Length when this length is known, which saves the computeLength() call
and the storage of intermediary values, leading to less GC)
Last week-end, I played with a Kerberos PDU (Ticket), and today I used
the AddRequest message.
It's more than 2 times faster.
I confirmed the result with JMH, which shown a 2x faster encoding
(20,529,987 encoding/s vs 9,393,737 encoding/s, on a smallest PDU, the
VLV request control)
Actually, modifying the code to adapt the various messages is not that
painful, it's more or less a matter of 1h per message, tests included.
I'll keep going for the moment, and I'll see if we can go any further
with the Decorator removal later on.
--
Emmanuel Lecharny
Symas.com
directory.apache.org
Re: ASN.1 encoding experiment
Posted by Emmanuel Lécharny <el...@gmail.com>.
A bit more about it.
Here is the code we are using for the AddRequest encoding :
public final class AddRequestDecorator extends
AbstractSingleReplyRequestDecorator<AddRequest> implements
AddRequest
{
/** The add request length */
private int addRequestLength;
/** The Entry length */
private int entryLength;
/** The list of all attributes length */
private List<Integer> attributesLength;
/** The list of all attributes Id bytes */
private List<byte[]> attributeIds;
/** The list of all vals length */
private List<Integer> valuesLength;
/** The bytes containing the Dn */
private byte[] dnBytes;
...
public int computeLength()
{
AddRequest addRequest = getDecorated();
Entry entry = addRequest.getEntry();
if ( entry == null )
{
throw new IllegalArgumentException( I18n.err(
I18n.ERR_05002_ENTRY_NULL_VALUE ) );
}
dnBytes = Strings.getBytesUtf8( entry.getDn().getName() );
int dnLen = dnBytes.length;
// The entry Dn
addRequestLength = 1 + TLV.getNbBytes( dnLen ) + dnLen;
// The attributes sequence
entryLength = 0;
if ( entry.size() != 0 )
{
attributesLength = new LinkedList<>();
attributeIds = new LinkedList<>();
valuesLength = new LinkedList<>();
// Compute the attributes length
for ( Attribute attribute : entry )
{
int localAttributeLength;
int localValuesLength;
// Get the type length
byte[] attributeIdBytes = Strings.getBytesUtf8(
attribute.getUpId() );
attributeIds.add( attributeIdBytes );
int idLength = attributeIdBytes.length;
localAttributeLength = 1 + TLV.getNbBytes( idLength ) +
idLength;
// The values
if ( attribute.size() != 0 )
{
localValuesLength = 0;
for ( Value value : attribute )
{
if ( value.getBytes() == null )
{
localValuesLength += 1 + 1;
}
else
{
int valueLength = value.getBytes().length;
localValuesLength += 1 + TLV.getNbBytes(
valueLength ) + valueLength;
}
}
localAttributeLength += 1 + TLV.getNbBytes(
localValuesLength ) + localValuesLength;
}
else
{
// No value : we still have to store the
encapsulating Sequence
localValuesLength = 1 + 1;
localAttributeLength += 1 + 1 + localValuesLength;
}
// add the attribute length to the attributes length
entryLength += 1 + TLV.getNbBytes( localAttributeLength
) + localAttributeLength;
attributesLength.add( localAttributeLength );
valuesLength.add( localValuesLength );
}
}
addRequestLength += 1 + TLV.getNbBytes( entryLength ) + entryLength;
// Return the result.
return 1 + TLV.getNbBytes( addRequestLength ) + addRequestLength;
}
...
public ByteBuffer encode( ByteBuffer buffer ) throws EncoderException
{
try
{
// The AddRequest Tag
buffer.put( LdapCodecConstants.ADD_REQUEST_TAG );
buffer.put( TLV.getBytes( addRequestLength ) );
// The entry
BerValue.encode( buffer, dnBytes );
// The attributes sequence
buffer.put( UniversalTag.SEQUENCE.getValue() );
buffer.put( TLV.getBytes( entryLength ) );
// The partial attribute list
Entry entry = getEntry();
if ( entry.size() != 0 )
{
int attributeNumber = 0;
// Compute the attributes length
for ( Attribute attribute : entry )
{
// The attributes list sequence
buffer.put( UniversalTag.SEQUENCE.getValue() );
int localAttributeLength = attributesLength.get(
attributeNumber );
buffer.put( TLV.getBytes( localAttributeLength ) );
// The attribute type
BerValue.encode( buffer, attributeIds.get(
attributeNumber ) );
// The values
buffer.put( UniversalTag.SET.getValue() );
int localValuesLength = valuesLength.get(
attributeNumber );
buffer.put( TLV.getBytes( localValuesLength ) );
if ( attribute.size() != 0 )
{
for ( Value value : attribute )
{
BerValue.encode( buffer, value.getBytes() );
}
}
else
{
BerValue.encode( buffer, Strings.EMPTY_BYTES );
}
// Go to the next attribute number
attributeNumber++;
}
}
return buffer;
}
catch ( BufferOverflowException boe )
{
throw new EncoderException( I18n.err(
I18n.ERR_08212_PDU_BUFFER_TOO_SMALL, boe.getMessage() ) );
}
}
And here the new encoder code :
public void encode( Asn1Buffer buffer )
{
int addRequestPos = buffer.getPos();
// The partial attribute list
Entry entry = getEntry();
if ( entry.size() != 0 )
{
// Compute the attributes length
for ( Attribute attribute : entry )
{
// The values
int attributePos = buffer.getPos();
if ( attribute.size() != 0 )
{
for ( Value value : attribute )
{
BerValue.encodeOctetString( buffer,
value.getBytes() );
}
}
else
{
BerValue.encodeOctetString( buffer,
Strings.EMPTY_BYTES );
}
TLV.encodeLength( buffer, attributePos - buffer.getPos() );
buffer.put( UniversalTag.SET.getValue() );
// The attribute ID
BerValue.encodeOctetString( buffer,
Strings.getBytesUtf8( attribute.getUpId() ) );
// The attributes list sequence
TLV.encodeLength( buffer, attributePos - buffer.getPos() );
buffer.put( UniversalTag.SEQUENCE.getValue() );
}
}
// The attributes sequence
TLV.encodeLength( buffer, addRequestPos - buffer.getPos() );
buffer.put( UniversalTag.SEQUENCE.getValue() );
// The entry DN
BerValue.encodeOctetString( buffer, dnBytes );
// The AddRequest Tag
TLV.encodeLength( buffer, addRequestPos - buffer.getPos() );
buffer.put( LdapCodecConstants.ADD_REQUEST_TAG );
}
I think it says it all...
--
Emmanuel Lecharny
Symas.com
directory.apache.org