THRIFT-5428 Prevent costly reallocations to improve performance
Client: Delphi
Patch: Jens Geyer
diff --git a/lib/delphi/src/Thrift.Protocol.Compact.pas b/lib/delphi/src/Thrift.Protocol.Compact.pas
index 665cfc4..424b267 100644
--- a/lib/delphi/src/Thrift.Protocol.Compact.pas
+++ b/lib/delphi/src/Thrift.Protocol.Compact.pas
@@ -80,6 +80,9 @@
STRUCT = $0C
);
+ private type
+ TEightBytesArray = packed array[0..7] of Byte;
+
strict private const
ttypeToCompactType : array[TType] of Types = (
Types.STOP, // Stop = 0,
@@ -190,7 +193,7 @@
class function intToZigZag( const n : Integer) : Cardinal;
//Convert a Int64 into little-endian bytes in buf starting at off and going until off+7.
- class procedure fixedLongToBytes( const n : Int64; var buf : TBytes);
+ class procedure fixedLongToBytes( const n : Int64; var buf : TEightBytesArray); inline;
strict protected
function GetMinSerializedSize( const aType : TType) : Integer; override;
@@ -240,7 +243,7 @@
// Note that it's important that the mask bytes are Int64 literals,
// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
// you just get a messed up Integer.
- class function bytesToLong( const bytes : TBytes) : Int64;
+ class function bytesToLong( const bytes : TEightBytesArray) : Int64; inline;
// type testing and converting
class function isBoolType( const b : byte) : Boolean;
@@ -319,10 +322,9 @@
// Write an i32 as a varint. Results in 1-5 bytes on the wire.
procedure TCompactProtocolImpl.WriteVarint32( n : Cardinal);
-var i32buf : TBytes;
- idx : Integer;
+var idx : Integer;
+ i32buf : packed array[0..4] of Byte;
begin
- SetLength( i32buf, 5);
idx := 0;
while TRUE do begin
ASSERT( idx < Length(i32buf));
@@ -339,7 +341,7 @@
n := n shr 7;
end;
- Transport.Write( i32buf, 0, idx);
+ Transport.Write( @i32buf[0], 0, idx);
end;
@@ -521,10 +523,10 @@
// Write a double to the wire as 8 bytes.
procedure TCompactProtocolImpl.WriteDouble( const dub: Double);
-var data : TBytes;
+var data : TEightBytesArray;
begin
fixedLongToBytes( DoubleToInt64Bits(dub), data);
- Transport.Write( data);
+ Transport.Write( @data[0], 0, SizeOf(data));
end;
@@ -580,10 +582,9 @@
// Write an i64 as a varint. Results in 1-10 bytes on the wire.
procedure TCompactProtocolImpl.WriteVarint64( n : UInt64);
-var varint64out : TBytes;
- idx : Integer;
+var idx : Integer;
+ varint64out : packed array[0..9] of Byte;
begin
- SetLength( varint64out, 10);
idx := 0;
while TRUE do begin
ASSERT( idx < Length(varint64out));
@@ -600,7 +601,7 @@
n := n shr 7;
end;
- Transport.Write( varint64out, 0, idx);
+ Transport.Write( @varint64out[0], 0, idx);
end;
@@ -627,9 +628,9 @@
// Convert a Int64 into 8 little-endian bytes in buf
-class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TBytes);
+class procedure TCompactProtocolImpl.fixedLongToBytes( const n : Int64; var buf : TEightBytesArray);
begin
- SetLength( buf, 8);
+ ASSERT( Length(buf) >= 8);
buf[0] := Byte( n and $FF);
buf[1] := Byte((n shr 8) and $FF);
buf[2] := Byte((n shr 16) and $FF);
@@ -829,11 +830,11 @@
// No magic here - just Read a double off the wire.
-function TCompactProtocolImpl.ReadDouble:Double;
-var longBits : TBytes;
+function TCompactProtocolImpl.ReadDouble : Double;
+var longBits : TEightBytesArray;
begin
- SetLength( longBits, 8);
- Transport.ReadAll( longBits, 0, 8);
+ ASSERT( SizeOf(longBits) = SizeOf(result));
+ Transport.ReadAll( @longBits[0], SizeOf(longBits), 0, SizeOf(longBits));
result := Int64BitsToDouble( bytesToLong( longBits));
end;
@@ -934,7 +935,7 @@
// Note that it's important that the mask bytes are Int64 literals,
// otherwise they'll default to ints, and when you shift an Integer left 56 bits,
// you just get a messed up Integer.
-class function TCompactProtocolImpl.bytesToLong( const bytes : TBytes) : Int64;
+class function TCompactProtocolImpl.bytesToLong( const bytes : TEightBytesArray) : Int64;
begin
ASSERT( Length(bytes) >= 8);
result := (Int64(bytes[7] and $FF) shl 56) or
@@ -1104,7 +1105,7 @@
procedure TestLongBytes;
procedure Test( const test : Int64);
- var buf : TBytes;
+ var buf : TCompactProtocolImpl.TEightBytesArray;
begin
TCompactProtocolImpl.fixedLongToBytes( test, buf);
ASSERT( TCompactProtocolImpl.bytesToLong( buf) = test, IntToStr(test));
diff --git a/lib/delphi/src/Thrift.Protocol.JSON.pas b/lib/delphi/src/Thrift.Protocol.JSON.pas
index 61cad8b..515d85c 100644
--- a/lib/delphi/src/Thrift.Protocol.JSON.pas
+++ b/lib/delphi/src/Thrift.Protocol.JSON.pas
@@ -32,6 +32,7 @@
Thrift.Configuration,
Thrift.Transport,
Thrift.Protocol,
+ Thrift.Stream,
Thrift.Utils;
type
@@ -832,7 +833,7 @@
function TJSONProtocolImpl.ReadJSONString( skipContext : Boolean) : TBytes;
-var buffer : TMemoryStream;
+var buffer : TThriftMemoryStream;
ch : Byte;
wch : Word;
highSurogate: Char;
@@ -841,7 +842,7 @@
tmp : TBytes;
begin
highSurogate := #0;
- buffer := TMemoryStream.Create;
+ buffer := TThriftMemoryStream.Create;
try
if not skipContext
then FContext.Read;
diff --git a/lib/delphi/src/Thrift.Serializer.pas b/lib/delphi/src/Thrift.Serializer.pas
index cb62603..8ee8a35 100644
--- a/lib/delphi/src/Thrift.Serializer.pas
+++ b/lib/delphi/src/Thrift.Serializer.pas
@@ -38,7 +38,7 @@
// Generic utility for easily serializing objects into a byte array or Stream.
TSerializer = class
strict private
- FStream : TMemoryStream;
+ FStream : TThriftMemoryStream;
FTransport : ITransport;
FProtocol : IProtocol;
@@ -59,7 +59,7 @@
// Generic utility for easily deserializing objects from byte array or Stream.
TDeserializer = class
strict private
- FStream : TMemoryStream;
+ FStream : TThriftMemoryStream;
FTransport : ITransport;
FProtocol : IProtocol;
@@ -92,7 +92,7 @@
begin
inherited Create;
- FStream := TMemoryStream.Create;
+ FStream := TThriftMemoryStream.Create;
adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE);
FTransport := TStreamTransportImpl.Create( nil, adapter, aConfig);
@@ -170,7 +170,7 @@
begin
inherited Create;
- FStream := TMemoryStream.Create;
+ FStream := TThriftMemoryStream.Create;
adapter := TThriftStreamAdapterDelphi.Create( FStream, FALSE);
FTransport := TStreamTransportImpl.Create( adapter, nil, aConfig);
diff --git a/lib/delphi/src/Thrift.Stream.pas b/lib/delphi/src/Thrift.Stream.pas
index 1668059..6c1320d 100644
--- a/lib/delphi/src/Thrift.Stream.pas
+++ b/lib/delphi/src/Thrift.Stream.pas
@@ -108,10 +108,52 @@
constructor Create( const aStream: IStream);
end;
+
+ TThriftMemoryStream = class(TMemoryStream)
+ strict protected
+ FInitialCapacity : NativeInt;
+ public
+ constructor Create( const aInitialCapacity : NativeInt = 4096);
+
+ // reimplemented
+ procedure Clear;
+
+ // make it publicly visible
+ property Capacity;
+ end;
+
+
+
implementation
uses Thrift.Transport;
+
+{ TThriftMemoryStream }
+
+constructor TThriftMemoryStream.Create( const aInitialCapacity : NativeInt);
+begin
+ inherited Create;
+ FInitialCapacity := aInitialCapacity;
+ Clear;
+end;
+
+
+procedure TThriftMemoryStream.Clear;
+// reimplemented to keep initial capacity
+begin
+ Position := 0;
+ Size := 0;
+
+ // primary goal: minimize costly reallocations (performance!)
+ // secondary goal: prevent costly ressource over-allocations
+ if (FInitialCapacity >= 1024*1024) // if we are talking about MB
+ or ((Capacity div 2) > FInitialCapacity) // or the allocated buffer is really large
+ or (Capacity < FInitialCapacity) // or we are actually below the limit
+ then Capacity := FInitialCapacity;
+end;
+
+
{ TThriftStreamAdapterCOM }
procedure TThriftStreamAdapterCOM.Close;
diff --git a/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas b/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas
index 6704c12..398e275 100644
--- a/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas
+++ b/lib/delphi/src/Thrift.Transport.MsxmlHTTP.pas
@@ -106,7 +106,7 @@
FReadTimeout := XMLHTTP_SENDRECV_TIMEOUT;
FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
- FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True);
+ FOutputStream := TThriftStreamAdapterDelphi.Create( TThriftMemoryStream.Create, True);
end;
function TMsxmlHTTPClientImpl.CreateRequest: IXMLHTTPRequest;
@@ -202,7 +202,7 @@
procedure TMsxmlHTTPClientImpl.Open;
begin
- FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True);
+ FOutputStream := TThriftStreamAdapterDelphi.Create( TThriftMemoryStream.Create, True);
end;
procedure TMsxmlHTTPClientImpl.Close;
@@ -217,7 +217,7 @@
SendRequest;
finally
FOutputStream := nil;
- FOutputStream := TThriftStreamAdapterDelphi.Create( TMemoryStream.Create, True);
+ FOutputStream := TThriftStreamAdapterDelphi.Create( TThriftMemoryStream.Create, True);
ASSERT( FOutputStream <> nil);
end;
end;
@@ -239,13 +239,13 @@
procedure TMsxmlHTTPClientImpl.SendRequest;
var
xmlhttp : IXMLHTTPRequest;
- ms : TMemoryStream;
+ ms : TThriftMemoryStream;
a : TBytes;
len : Integer;
begin
xmlhttp := CreateRequest;
- ms := TMemoryStream.Create;
+ ms := TThriftMemoryStream.Create;
try
a := FOutputStream.ToArray;
len := Length(a);
diff --git a/lib/delphi/src/Thrift.Transport.WinHTTP.pas b/lib/delphi/src/Thrift.Transport.WinHTTP.pas
index b0f32ef..746611d 100644
--- a/lib/delphi/src/Thrift.Transport.WinHTTP.pas
+++ b/lib/delphi/src/Thrift.Transport.WinHTTP.pas
@@ -41,7 +41,7 @@
strict private
FUri : string;
FInputStream : IThriftStream;
- FOutputMemoryStream : TMemoryStream;
+ FOutputMemoryStream : TThriftMemoryStream;
FDnsResolveTimeout : Integer;
FConnectionTimeout : Integer;
FSendTimeout : Integer;
@@ -127,7 +127,7 @@
FSecureProtocols := DEFAULT_THRIFT_SECUREPROTOCOLS;
FCustomHeaders := TThriftDictionaryImpl<string,string>.Create;
- FOutputMemoryStream := TMemoryStream.Create;
+ FOutputMemoryStream := TThriftMemoryStream.Create;
end;
destructor TWinHTTPClientImpl.Destroy;
@@ -269,7 +269,7 @@
procedure TWinHTTPClientImpl.Open;
begin
FreeAndNil( FOutputMemoryStream);
- FOutputMemoryStream := TMemoryStream.Create;
+ FOutputMemoryStream := TThriftMemoryStream.Create;
end;
procedure TWinHTTPClientImpl.Close;
@@ -284,7 +284,7 @@
SendRequest;
finally
FreeAndNil( FOutputMemoryStream);
- FOutputMemoryStream := TMemoryStream.Create;
+ FOutputMemoryStream := TThriftMemoryStream.Create;
ASSERT( FOutputMemoryStream <> nil);
end;
end;
diff --git a/lib/delphi/src/Thrift.Transport.pas b/lib/delphi/src/Thrift.Transport.pas
index 79fc977..558b65e 100644
--- a/lib/delphi/src/Thrift.Transport.pas
+++ b/lib/delphi/src/Thrift.Transport.pas
@@ -334,8 +334,8 @@
strict private
FStream : IThriftStream;
FBufSize : Integer;
- FReadBuffer : TMemoryStream;
- FWriteBuffer : TMemoryStream;
+ FReadBuffer : TThriftMemoryStream;
+ FWriteBuffer : TThriftMemoryStream;
strict protected
procedure Write( const pBuf : Pointer; offset: Integer; count: Integer); override;
function Read( const pBuf : Pointer; const buflen : Integer; offset: Integer; count: Integer): Integer; override;
@@ -450,8 +450,8 @@
strict protected type
TFramedHeader = Int32;
strict protected
- FWriteBuffer : TMemoryStream;
- FReadBuffer : TMemoryStream;
+ FWriteBuffer : TThriftMemoryStream;
+ FReadBuffer : TThriftMemoryStream;
procedure InitWriteBuffer;
procedure ReadFrame;
@@ -1053,8 +1053,8 @@
inherited Create;
FStream := aStream;
FBufSize := aBufSize;
- FReadBuffer := TMemoryStream.Create;
- FWriteBuffer := TMemoryStream.Create;
+ FReadBuffer := TThriftMemoryStream.Create(FBufSize);
+ FWriteBuffer := TThriftMemoryStream.Create(FBufSize);
end;
destructor TBufferedStreamImpl.Destroy;
@@ -1379,16 +1379,11 @@
Result := InnerTransport.IsOpen;
end;
-type
- TAccessMemoryStream = class(TMemoryStream)
- end;
-
procedure TFramedTransportImpl.InitWriteBuffer;
const DUMMY_HEADER : TFramedHeader = 0;
begin
FreeAndNil( FWriteBuffer);
- FWriteBuffer := TMemoryStream.Create;
- TAccessMemoryStream(FWriteBuffer).Capacity := 1024;
+ FWriteBuffer := TThriftMemoryStream.Create(1024);
FWriteBuffer.Write( DUMMY_HEADER, SizeOf(DUMMY_HEADER));
end;
@@ -1448,7 +1443,7 @@
InnerTransport.ReadAll( buff, 0, size );
FreeAndNil( FReadBuffer);
- FReadBuffer := TMemoryStream.Create;
+ FReadBuffer := TThriftMemoryStream.Create(1024);
if Length(buff) > 0
then FReadBuffer.Write( Pointer(@buff[0])^, size );
FReadBuffer.Position := 0;