What a bad ReallocMem can do to Midas

By | January 20, 2009

The TClientDataSet isn’t known for its speed. It becomes slower and slower if you add more and more records. But what is the reason for this performance hit? After some research (F12 is a nice key if you want to know which function either gets called very often or consumes a lot of time) I nailed it down to the memory manager that is used inside the Midas code. (BTW: Thank’s CodeGear for compiling MidasLib.dcu with C/C++ source line information what makes reading assembler code a lot easier).

By using the MidasLib.dcu instead of the external Midas.dll the FastMM memory manager is used but this doesn’t give any significant performance improvement. So you would think the memory manager can’t be the bottle neck. But wait, there is a function (I call it) MidasRealloc. And with FastMM you would think it calls System.ReallocMemory. But that isn’t the case. The function looks like this: (translated from assembler to Delphi)

function MidasRealloc(P: Pointer; OldSize, NewSize: LongWord): Pointer; stdcall;
begin
  Result := AllocMem(NewSize); // => GetMem+FillChar
  if (Result <> nil) and (P <> nil) then
  begin
    Move(P^, Result^, OldSize);
    FreeMem(P);
  end;
end;

As you can see the function never calls ReallocMemory. Instead it does it the hard way for every call. Allocate new memory, zero the new memory, copy the previous content and release the old memory block. On the first look the AllocMem could be replaced by GetMem with a FillChar that only zero-fills the “Gap := NewSize – OldSize” block and not the whole memory block. But at a second look you realize that this is nothing else than a really bad ReallocMemory implementation. FastMM’s ReallocMemory is very fast because it doesn’t need to realloc memory blocks and copy memory all the time. Instead it has some inteligence in it to reduce the memory block moving.

Let’s see what can be done against this bad implementation. Replacing MidasRealloc alone woudn’t work because the memory manager has also a malloc(), a calloc() and a free() function. But all in all the replacements that call into FastMM aren’t that hard to write, especially the MidasRealloc becomes less code. The main problem is to find these function in the process space in order to patch them. But I already have the knowledge to find them. And I’ve already have a unit that makes the Midas a lot faster.

Testapplication from QC 7102
[rename Vessels200.xml to Vessels.xml in the source code and put a “for 1 to 10 do” loop before the ClientDataSet1.AppendData”]

Delphi 2009’s MidasLib unit:
1. Call: 858ms
2. Call: 1966ms
3. Call: 3182ms
4. Call: 4414ms
5. Call: 5678ms

Delphi 2009’s MidasLib unit with my MidasSpeedFix.pas unit:
1. Call: 406ms
2. Call: 265ms
3. Call: 374ms
4. Call: 312ms
5. Call: 281ms

13 thoughts on “What a bad ReallocMem can do to Midas

  1. Per Bakkendorff

    Did you update the QC entry with this valuable information ? It seems like the Delphi team hasn’t done anything yet to this entry – well it’s an open entry, but no solution.
    Per

  2. Mason Wheeler

    This is great! Is MidasSpeedFix.pas available for download? Also, while you’re poking around in MidasLib’s memory management, any way you could address the memory leak documented in QC #70409? It only leaks a few bytes at a time, but with hundreds or thousands of records, accessed hundreds of times, it adds up.

  3. Ali

    That’s cool! Thank you for all bug fixings you do on Delphi. I hope you publish this unit later.

    Regards

  4. Mihail

    There is no test application on QC. Only text in field “Attachment”, no links 🙁

  5. Mihail

    I have an apllication build with runtime packages and you fix doesn’t work %(. I try to add MidasSpeedFix.pas to ‘exe’ project and to ‘bpl’ project, but Org_MidasMalloc variable is always goes nil. Any ideas?

    Sorry for my English 😉

  6. Mihail

    Delphi 2007 December Update (Version 11.0.2902.10471). I use Midas.dll.
    When I try to use MidasLib, all start working, thanks for the tip.

    Unfortunately i don’t get any performance boost. Maybe there are too small “hit count” of MidasRealloc during my application work (~4000 on start, ~500 per minute on working). Thanks anyway.

  7. Alexandre Barbosa Simplício

    Esta funcao so funciona com o Delphi 2009?

    Obrigado.Pela.Atencao;

  8. Murat Ak

    I wrote a test project very easily and it is working. %50 performance improvement for my test project. Loading cds (binary) file, and inserting another table.
    I do not know my real project performance improvement. But i think this is right way for improve midas performance.

    Thanks so much.

  9. Olaf Monien

    Extremely cool!

    Just one side question: What does [F12] do for you? Here I’m using it for switching between source and design. I guess thats not what tells you about time spent on certain method calls 😉

  10. yavfast

    Before append:

    _DS.AppendRecord([Null]);

    for i:=1 to _BufLevel do
    _DS.AppendData(_DS.Data, True);

    _DS.EmptyDataSet;

  11. Farshad Mohajeri

    Excellent job! You could nail it by just looking at the binaries. Borland/CodeGear with full access to the source code hadn’t care to produce a solution for this slowness even after five years. Although they’ve renewed and enhanced DataSnap in D2009, TCDS is still the same old slow memory dataset.

    All developers who need to do some serious job with in-memory datasets have already switched to faster 3rd party alternative memory dataset solutions. Maybe it will take another five years for CG to actually fix this bug, who knows!

Comments are closed.