Delphi XE3 - performance issue with TDataSet - delphi

Data processing with XE3 programs take up to 10 times more than with same programs compiled with XE2. This is known issue (probably refers mainly to TStringField), reported to QC 111942, but it is not fixed yet. Does anybody have a fix / workaround for this issue?
TIA Branko

Same in XE5. Plus extra traffic and all this client-server thing require >5 Mbit per second (!) to work normally. I am using only TFDConnection and TFDQuery. Specially for MySQL the speed is the same with Delphi components and with third-party driver (libmysql.dll). If you have no FireDAC you can replace TFDQuery with TSQLQuery. Here is a procedure how to fill a string grid:
procedure SelGrid(sql:ansiString;Q:TFDQuery;grid:TStringGrid);
var i: integer;
begin
Q.Close;
Q.SQL.Text:='';
Q.Open(sql);
grid.ColCount:=Q.FieldCount;
grid.RowCount:=1;
while not Q.Eof do begin
for i := 0 to grid.ColCount-1 do grid.Cells[i,grid.RowCount-1]:=Q.Fields.Fields[i].AsString;
grid.RowCount:=grid.RowCount+1;
Q.Next;
end;
Q.Close;
if grid.RowCount>1 then grid.RowCount:=grid.RowCount-1;
grid.Row:=0;
//AutoSizeGridColumns(grid,30,200);
end;
This is VCL string grid. Of course you muse deal with updates and so on, but you'll have no more performance problems.

Related

Load TGPBitmap from MemoryStream

I have been asked to correct an issue (not related to this question) in a legacy Delphi program. After fixing some issues with missing components, I am now stuck with some GDI Plus functionality, which stops me from compiling the program. One of the functions where this is used is:
function TDownLoadItem.LoadRawBitmapFromStream(var bm: TBitmap): Boolean;
var
image: TGPBitmap;
begin
Result := False;
if Content.Size = 0 then
exit;
// NOTE: Content is a TMemoryStream, declared globally.
image := GDIPlusHelper.LoadBitmapFromStream(Content); // <== This is where the problem is....
try
bm.Width := image.GetWidth;
bm.Height := image.GetHeight;
with TGPGraphics.Create(bm.Canvas.Handle) do
try
DrawImage(image, 0, 0, image.GetWidth, image.GetHeight);
Result := True;
finally
Free;
end;
finally
image.Free;
end;
end;
I think (not sure) the last Delphi version used was 2006, I am on Delphi Rio 10.3.
Online I have managed to find GDI+ 1.2, but this does not solve the problem. The procedure LoadBitmapFromStream does not exit in these libraries. GDIPlusHelper was apparently renamed to GDIPlusHelpers and most code has changed from classes to interfaces. I suspect an older edition of the GDI Plus libraries were used, but I cannot find these.
Reworking the code would be too complex as it would require Content to be an IStream instead of a TMemoryStream. Also, simply using a TBitmap is not feasible either as other code (not shown) uses functionality specific to TGPBitmap (e.g. RotateFlip).
Any suggestions on how to fix/work around this? Thanks in advance!

Delphi Webmodule : What happened to TWebModuleList?

My program is a Delphi ISAPI DLL built with Delphi 2007.
I'm migrating to Delphi 10.2 Tokyo and TWebModuleList is gone from Web.WebReq.
With D2007, when the application starts, the following code is called in order to pre instanciate a pool of 5 WebModules.
procedure TSWebApplication.CreationWebModules;
var
i: integer;
TabDataModules : array of TWebModuleList;
begin
SetLength(TabDataModules,1);
TabDataModules[0] := ActivateWebModules;
if Assigned(TabDataModules[0]) then
TabDataModules[0].AutoCreateModules;
SetLength(TabDataModules, NbInstances);
for i := 1 to NbInstances - 1 do
begin
TabDataModules[i] := ActivateWebModules;
if Assigned(TabDataModules[i]) then
TabDataModules[i].AutoCreateModules;
end;
for i := 0 to NbInstances - 1 do
DeActivateWebModules(TabDataModules[i]);
end;
This way, instead of having the (first 5) WebModules created on demand, they are created when the application starts, then deactivated and auto activated on demand.
I know WebModules are managed by IIS and we don't necessary need this behavior, but it is the way it worked (pretty well).
TWebModuleList seems gone since Delphi XE8 (no page on docwiki.embarcadero.com whereas it was still there on the XE7 site).
I couldn't find ANY changelog, or other information on why it is gone et how we can build the same behavior...
Does anyone have any information?
Thanks

Store array in TQueue possible?

Having problem storing an array in a TQueue. Any idea where I go wrong?
Code works fine in Delphi XE 5 but not in Delphi 10 Seattle.
(I can't decide if this is a bug or how it should work. Tried searching embarcadero for clues but failed.)
procedure TForm1.Button1Click(Sender: TObject);
var
FData: TQueue<TBytes>;
FsData: TQueue<String>;
arr: TBytes;
begin
FData := TQueue<TBytes>.Create;
FsData := TQueue<String>.Create;
try
setlength(arr, 3);
arr[0] := 1;
arr[1] := 2;
arr[2] := 3;
FData.Enqueue(arr);
Memo1.Lines.Add('Count, array:' + IntToStr(FData.Count)); // 0?
FsData.Enqueue('asada');
Memo1.Lines.Add('Count, string:' + IntToStr(FsData.Count)); // 1
finally
FData.Free;
FsData.Free;
end;
end;
This is a defect introduced in XE8. Here's the simplest reproduction that I can produce.
{$APPTYPE CONSOLE}
uses
System.Generics.Collections;
var
Queue: TQueue<TArray<Byte>>;
begin
Queue := TQueue<TArray<Byte>>.Create;
Queue.Enqueue(nil);
Writeln(Queue.Count);
end.
The output is 1 in XE7 and 0 in XE8 and Seattle.
This has already been reported to Embarcadero: RSP-13196.
The implementation of Enqueue looks like this:
procedure TQueue<T>.Enqueue(const Value: T);
begin
if IsManagedType(T) then
if (SizeOf(T) = SizeOf(Pointer)) and (GetTypeKind(T) <> tkRecord) then
FQueueHelper.InternalEnqueueMRef(Value, GetTypeKind(T))
else
FQueueHelper.InternalEnqueueManaged(Value)
else
case SizeOf(T) of
1: FQueueHelper.InternalEnqueue1(Value);
2: FQueueHelper.InternalEnqueue2(Value);
4: FQueueHelper.InternalEnqueue4(Value);
8: FQueueHelper.InternalEnqueue8(Value);
else
FQueueHelper.InternalEnqueueN(Value);
end;
end;
When T is a dynamic array, the FQueueHelper.InternalEnqueueMRef branch is chosen. This in turn looks like this:
procedure TQueueHelper.InternalEnqueueMRef(const Value; Kind: TTypeKind);
begin
case Kind of
TTypeKind.tkUString: InternalEnqueueString(Value);
TTypeKind.tkInterface: InternalEnqueueInterface(Value);
{$IF not Defined(NEXTGEN)}
TTypeKind.tkLString: InternalEnqueueAnsiString(Value);
TTypeKind.tkWString: InternalEnqueueWideString(Value);
{$ENDIF}
{$IF Defined(AUTOREFCOUNT)}
TTypeKind.tkClass: InternalEnqueueObject(Value);
{$ENDIF}
end;
end;
Note that there is no entry for TTypeKind.tkDynArray. Because these two methods are inlined, the inliner manages to compress it all down to nothing. No action is performed when you Enqueue a dynamic array.
Back in the good old days of XE7 the code looked like this:
procedure TQueue<T>.Enqueue(const Value: T);
begin
if Count = Length(FItems) then
Grow;
FItems[FHead] := Value;
FHead := (FHead + 1) mod Length(FItems);
Inc(FCount);
Notify(Value, cnAdded);
end;
No scope for type specific defects there.
I don't think that there's an easy workaround for you. Perhaps the most expedient way to proceed is to take the code for the XE7 TQueue and use that in place of the broken implementation from XE8 and Seattle. For the record, I've given up on the Embarcadero generic collections and use my own classes.
The back story here is that in XE8, Embarcadero decided to address a deficiency in their implementation of generics. Whenever you instantiate a generic type, copies of all the methods are created. For some methods, identical code is generated for different instantiations.
So it is quite common for TGeneric<TFoo>.DoSomething and TGeneric<TBar>.DoSomething to have identical code. Other compilers for other languages, C++ templates, .net generics, etc., recognise this duplication and merge together identical generic methods. The Delphi compiler does not. The end result is a larger executable than strictly necessary.
In XE8 Embarcadero decided to tackle this in what I regard was utterly the wrong way. Instead of attacking the root cause of the issue, the compiler, they decided to change the implementation of their generic collection classes. If you look at the code in Generics.Collections, you will see that it has been completely re-written in XE8. Where previously the code from XE7 and earlier was readable, from XE8 it is now exceedingly complex and opaque. This decision had the following consequences:
The complex code contained many errors. Many of these were found shortly after XE8 was released and have been fixed. You have stumbled upon another defect. One thing that we have learnt is that Embarcadero's internal test suite does not exercise their collection classes sufficiently. It is manifestly clear that their tests are inadequate.
By changing their library rather than the compiler, they have patched up the RTL classes. The original issue with generic code bloat remains for third party classes. Had Embarcadero fixed the issue at source then not only could they have retained the simple and correct collection class code from XE7, but all third generic code would have benefited.

Otaining bug fixes for Delphi

Does anyone know how (if) I can obtain bug fixes for Delphi for known, fixed bugs (specifically QC report #125506 relating to indexes in Clientdatasets)
I understand that it has been fixed in XE7 but I object(!!!) to paying £1,000+ to update from XE6 to XE7 when I have only had XE6 for a matter of months, I have spent the time identifying the bug and the ONLY reason I have for moving from XE6 to XE7 is to fix the bug (rant over)!
Apologies for posting this as an "answer" but there are a few things worth mentioning that won't fit comfortably in a comment (or two).
As you've probably gathered, the "Resolution comments" on QC #125506 don't say anything useful, in particular about what was changed or where e.g. in DBClient.Pas or Midas.Dll. I've just run Beyond Compare on the sources of DBClient.Pas in XE6 & XE7, and the changes are minimal: The declaration of TCustomClientDataSet has had a local class declaration of "TPersistDataPacket" added to it and there are a few consequential changes, but whether they bear on QC #125506 is impossible to say. I wondered about quoting the changes here, but decided not to in view of possible copyright or T&C problems.
The versions of MidasLib.Pas in XE6 and XE7 are identical, but the size of the 32-bit release build of MidasLib.Dcu has increased marginally, from 241447 to 241646 bytes. Oddly, Midas.Dll has actually reduced in size, from 451960 to 437632 bytes.
A couple of obvious suggestions:
I'm not sure if the trial version of XE7 includes the Datasnap stuff, but in view of the expense, it would definitely be worth "trying before you buy" if you can manage it. If it doesn't include Datasnap, you might nevertheless see if you can get hold of a copy of the XE7 Midas.Dll - once upon a time Borland-as-was was quite liberal in allowing the latest Midas.Dll to be used with earlier versions. Might be worthwhile asking about both these points on the EMBA newgroups.
In QC #125506, the reporter seems to have run into the problem when using the CDS IndexFieldNames property. If that's how your getting it, have you tried defining a persistent IndexDef instead? The following code works for me (tested on the Authors table of the Sql Server Pubs demo database).
Dynamically adding/using an IndexDef.
procedure TDefaultForm.AddIndex(AFieldName: String; CaseInsensitive: Boolean);
var
AIndexDef : TIndexDef;
AIndexName : String;
Options : TIndexOptions;
BM : TBookmark;
begin
if CDS1.IndexDefs.GetIndexForFields(AFieldName, CaseInsensitive) = Nil then begin
BM := CDS1.GetBookmark;
try
CDS1.DisableControls;
AIndexName := 'By' + AFieldName;
Options := [];
if CaseInsensitive then
Options := Options + [ixCaseInsensitive];
AIndexDef := TIndexDef.Create(CDS1.IndexDefs, AIndexName, AFieldName, Options);
CDS1.IndexName := AIndexName;
finally
CDS1.GotoBookmark(BM);
CDS1.FreeBookmark(BM);
CDS1.EnableControls;
end;
end;
end;
procedure TDefaultForm.btnAddClick(Sender: TObject);
begin
AddIndex('au_lname', True);
end;

What is the recommended support for mouse-wheel capability in Delphi XE2?

I am trying to tidy up my code now that Delphi XE2 is available - my code dates from Borland Pascal 7 so there are lots of 'old' (but working!) Win32 techniques and natually I have platform independence in mind too. Support for the mouse wheel has come up before here with several prior questions 1 2 and 3. As with some of these answers, my own solution is a simple mouse message intercept using a TApplicationEvents component:
procedure TForm6.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
procedure ProcessMouseWheelMessage;
begin
Msg.message := WM_KEYDOWN;
Msg.lParam := 0;
If Integer(Msg.wParam) > 0 then
Msg.wParam := VK_UP
else
Msg.wParam := VK_DOWN;
Handled := False;
end;
begin
Case Msg.message of
WM_MOUSEWHEEL :
ProcessMouseWheelMessage;
end;
end;
I revisited this code today because 'Msg.wParam' is now NativeInt, breaking use of negative Msg.wParam values in the above code unless you use Integer(Msg.wParam). It made me notice that I had not seen any really definitive use of the mouse wheel for Delphi code - terrible when all mice now have wheels and Delphi is at the 'cutting edge' again! I would have expected a property, a component or some other more 'exposed' solution, and what about Fire Monkey wheel support?
Do I carry on with my solution or is there a better way?
In XE2 (and indeed all the recent releases) you don't need to do anything. The standard controls support mouse wheel scrolling out of the box. Just get rid of this old code.
Delphi components that have a Windows handle (descendents of TWinControl) have the OnMouseWheel, OnMouseWheelUp and OnMouseWheelDown events.
If you want to add a mousewheel event to a control that does not descent from TWinControl, see this article: http://delphi.about.com/od/delphitips2010/qt/timage-handling-mouse-wheel-messages.htm

Resources