Recent

Author Topic: FPC memory manager vs C std library  (Read 5088 times)

Ryan J

  • Full Member
  • ***
  • Posts: 141
Re: FPC memory manager vs C std library
« Reply #15 on: July 30, 2023, 05:40:42 pm »
@Warfley it sounds like FPC could actually benefit from a stack based allocator since you're doing lots of little allocations that are deallocated soon in the same scope? Either way as discussed many times the way FPC allocates classes is a bad design anyways since it forces you in to this pattern of needless allocations.

I've never used custom allocators in  C++, are they the same as overriding New/FreeInstance in TObject?

BSaidus

  • Hero Member
  • *****
  • Posts: 651
  • lazarus 1.8.4 Win8.1 / cross FreeBSD
Re: FPC memory manager vs C std library
« Reply #16 on: July 30, 2023, 07:58:15 pm »
Hello,
What about my case.
I've created c CGI program for FreeBSD-aarch64 platform. The program compile and run on this plateform.
But, when compile in debug mode and run, the is a memory leak when compiled with default fpc memory manager.
If I use 'cmem' in the begin of each uses clause, the leak disappear.
Code: Pascal  [Select][+][-]
  1. // ----------------------------------------------------------------------
  2. // CMEM
  3. // ----------------------------------------------------------------------
  4. root@uc-orpi:~/mem # ./indexcmem.cgi
  5. Status: 400 Application error EHTTP
  6. Content-Length: 461
  7. Content-Type: text/html
  8.  
  9. <html><head><title>: Module Error</title></head>
  10.  
  11. <body>
  12. <center><hr><h1>: ERROR</h1><hr></center><br><br>
  13. The application encountered the following error:<br>
  14. <ul>
  15. <li>Error:  <b>No REQUEST_METHOD passed from server.</b>
  16. <li> Stack trace:<br>
  17.   $00000000004A78C0<br>
  18.   $00000000004A7A1C<br>
  19.   $00000000004AD85C<br>
  20.   $00000000004AD8DC<br>
  21.   $00000000004ABB30<br>
  22.   $00000000004AD1E8<br>
  23.   $00000000004E1048<br>
  24.   $0000000000402D70<br>
  25. </ul><hr>
  26. </body></html>
  27. Heap dump by heaptrc unit of "/root/mem/indexcmem.cgi"
  28. 0 memory blocks allocated : 0/0
  29. 0 memory blocks freed     : 0/0
  30. 0 unfreed memory blocks : 0
  31. True heap size : 1114112 (32 used in System startup)
  32. True free heap : 1114080
  33.  
  34.  
  35. // ----------------------------------------------------------------------
  36. // FPC Default
  37. // ----------------------------------------------------------------------
  38. root@uc-orpi:~/mem # ./indexfpc.cgi
  39. Status: 400 Application error EHTTP
  40. Content-Length: 461
  41. Content-Type: text/html
  42.  
  43. <html><head><title>: Module Error</title></head>
  44.  
  45. <body>
  46. <center><hr><h1>: ERROR</h1><hr></center><br><br>
  47. The application encountered the following error:<br>
  48. <ul>
  49. <li>Error:  <b>No REQUEST_METHOD passed from server.</b>
  50. <li> Stack trace:<br>
  51.   $00000000004A76A0<br>
  52.   $00000000004A77FC<br>
  53.   $00000000004AD63C<br>
  54.   $00000000004AD6BC<br>
  55.   $00000000004AB910<br>
  56.   $00000000004ACFC8<br>
  57.   $00000000004E0E28<br>
  58.   $0000000000402D70<br>
  59. </ul><hr>
  60. </body></html>
  61. An unhandled exception occurred at $00000000004299BC:
  62. EAccessViolation: Access violation
  63.   $00000000004299BC
  64.   $0000000082E06494
  65.   $0000000082E05BBC
  66.   $0000FFFFFFFFF1A0
  67.   $000000000042BB84
  68.   $000000000041D238
  69.   $0000000000414DBC
  70.   $0000000000414BD4
  71.   $0000000000414C04
  72.   $00000000004976E8
  73.   $000000000049778C
  74.   $00000000004A25F8
  75.   $000000000041BF00
  76.   $000000000041C470
  77.   $000000000041C4CC
  78.   $0000000000402D8C
  79.  
  80. Heap dump by heaptrc unit of "/root/mem/indexfpc.cgi"
  81. 65 memory blocks allocated : 2341/2424
  82. 60 memory blocks freed     : 2109/2192
  83. 5 unfreed memory blocks : 232
  84. True heap size : 1277952 (32 used in System startup)
  85. True free heap : 1276576
  86. Should be : 1276728
  87. Call trace for block $000000008DBE3100 size 128
  88.   $000000000042BC1C
  89.   $000000000041D2A8
  90.   $000000000041709C
  91.   $0000000000417270
  92.   $000000000049D94C
  93.   $000000000041C690
  94.   $000000000041C714
  95.   $000000000041C794
  96.   $00000000004299BC
  97.   $0000000082E06494
  98.   $0000000082E05BBC
  99.   $0000FFFFFFFFF1A0
  100.   $000000000042BB84
  101.   $000000000041D238
  102.   $0000000000414DBC
  103.   $0000000000414BD4
  104. Call trace for block $0000000088BC4600 size 40
  105.   $000000000042CA90
  106.   $000000000041D288
  107.   $0000000000417020
  108.   $0000000000417270
  109.   $000000000049D94C
  110.   $000000000041C690
  111.   $000000000041C714
  112.   $000000000041C794
  113.   $00000000004299BC
  114.   $0000000082E06494
  115.   $0000000082E05BBC
  116.   $0000FFFFFFFFF1A0
  117.   $000000000042BB84
  118.   $000000000041D238
  119.   $0000000000414DBC
  120.   $0000000000414BD4
  121. Call trace for block $0000000088BC4700 size 24
  122.   $000000000041D17C
  123.   $0000000000414D78
  124.   $000000000049C764
  125.   $000000000049D768
  126.   $000000000041C690
  127.   $000000000041C714
  128.   $000000000041C794
  129.   $00000000004299BC
  130.   $0000000082E06494
  131.   $0000000082E05BBC
  132.   $0000FFFFFFFFF1A0
  133.   $000000000042BB84
  134.   $000000000041D238
  135.   $0000000000414DBC
  136.   $0000000000414BD4
  137.   $0000000000414C04
  138. Call trace for block $0000000089A55100 size 16
  139.   $000000000041D2F0
  140.   $000000000041F7B8
  141.   $00000000004BE2A4
  142.   $000000000041BDC8
  143.   $0000000000402CBC
  144. Call trace for block $0000000088BC4100 size 24
  145.   $000000000041D2F0
  146.   $000000000041CBC8
  147.   $000000000041F7B0
  148.   $00000000004BE2A4
  149.   $000000000041BDC8
  150.   $0000000000402CBC
  151. ...
  152.  
  153.  

So, Knowing that I did not allocated any memory, just a simple CGI Module.


Thanks.

lazarus 1.8.4 Win8.1 / cross FreeBSD
dhukmucmur vernadh!

Warfley

  • Hero Member
  • *****
  • Posts: 2040
Re: FPC memory manager vs C std library
« Reply #17 on: July 30, 2023, 10:33:30 pm »
I've never used custom allocators in  C++, are they the same as overriding New/FreeInstance in TObject?
No it's on a per instance basis:
Code: C  [Select][+][-]
  1. std::allocator<MyType> alloc; // As example just use standard allocator
  2. MyType *instance = new(alloc.allocate(1)) MyType(args); // Calls constructor using allocator
  3. // use instance
  4. // ...
  5. instance->~MyType(); // Call destructor
  6. alloc.deallocate(instance, 1); // Free memory

This then can be encapsulated for example in std::shared_ptr (reference counted pointer implementation):
Code: C  [Select][+][-]
  1. std::allocator<MyType> alloc; // As example just use standard allocator
  2. auto instance = std::allocate_shared<MyType>(alloc, args); // calls constructor and with args
  3. // instance will be freed automatically because of shared_ptr

Note that there is just one tiny difference on why this works in C++ and not in Pascal. You can call the constructor and destructor individually and independently from the memory management. You can do in Pascal for the constructor but not the Destructor:
Code: Pascal  [Select][+][-]
  1. instance := TMyClass(GetMem(TMyClass.InstanceSize)); // Get memory without calling constructor
  2. instance.Create(Args); // Call constructor independently from memory management
  3. instance.Destroy; // Cannot easiely seperate destructor from free

One workaround is to actually make a copy of the VMT, and override FreeInstance on runtime. I've built a prototype in https://github.com/Warfley/FpAllocators
Code: Pascal  [Select][+][-]
  1. function TBaseAllocator.Allocate(cls: TClass): TObject;
  2. var
  3.   AllocMem: PAllocMem;
  4.   AVMTSize: SizeInt;
  5. begin
  6.   [...]
  7.   AVMTSize := VMTSize(cls);
  8.   AllocMem := FGetMem(SizeOf(TAllocMem) + AVMTSize + cls.InstanceSize); // Allocates memory for class + copy of VMT
  9.   [...]
  10.   Result := InitClass(AllocMem, AVMTSize, cls);
  11. end;
  12.  
  13. function TBaseAllocator.InitClass(AllocMem: PAllocMem; AVMTSize: SizeInt; cls: TClass): TObject;
  14. begin
  15.   // [...] Initialization stuff
  16.   // Create class instance
  17.   Result := cls.InitInstance(AllocMem^.GetObject);
  18.   // Override VMT
  19.   Move(PVMT(cls)^, AllocMem^.VMT^, AVMTSize);
  20.   PPVmt(Result)^ := AllocMem^.VMT;
  21.   // Register custom FreeInstance
  22.   AllocMem^.VMT^.vFreeInstance := @CustomFreeInstance;
  23. end;
Which then allows stuff like this:
Code: Pascal  [Select][+][-]
  1. procedure TestStack;
  2. var
  3.   alloc: TStackAllocator; // Frees all instances when it goes out of scope (end of function)
  4.   sl: TStrings;
  5. begin
  6.   sl := alloc.Alloc<TStringList>.Create; // Memory allocated by allocator, and constructor called independently
  7.   sl.Add('Foo');
  8.   Smack;
  9.   sl.Add('Bar');
  10.   WriteLn(sl.Text);
  11. end;
  12.  
  13. procedure TestHeap;
  14. var
  15.   alloc: THeapAllocator;
  16.   sl: TStrings;
  17. begin
  18.   sl := alloc.Alloc<TStringList>.Create;
  19.   sl.Add('Foo');
  20.   Smack;
  21.   sl.Add('Bar');
  22.   WriteLn(sl.Text);
  23. end;

But this is of course just a proof of concept and shouldn't be used in any real code. It just shows what could be possible

Leledumbo

  • Hero Member
  • *****
  • Posts: 8835
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: FPC memory manager vs C std library
« Reply #18 on: July 31, 2023, 07:52:09 am »
Hello,
What about my case.
CGI processes are not long running, they die as soon as the response has been sent. Memory leak is not a problem there.

Ryan J

  • Full Member
  • ***
  • Posts: 141
Re: FPC memory manager vs C std library
« Reply #19 on: August 01, 2023, 08:06:45 pm »
But this is of course just a proof of concept and shouldn't be used in any real code. It just shows what could be possible

It looks like an interesting idea. Why not use it in production code? Is there some reason to believe it will fail? The syntax is getting C++ levels of ugly thought . :)

Warfley

  • Hero Member
  • *****
  • Posts: 2040
Re: FPC memory manager vs C std library
« Reply #20 on: August 02, 2023, 06:18:09 pm »
The copying of the VMT is using undocumented assumptions about the format of the VMT, which could change at any fpc update or potentially between different optimization levels.

And the stack allocator is assuming memory layout of the stack which also is not documented an can be subject to things like inclining, exception handling etc. It already breakers in the code above when trying to call the stack function twice. I assume this is because strings add a hidden try-except frame which I break, but to be fully honest, I have no idea why it doesn't work.

Any such deep changes can only be implemented correctly with support by the compiler.

Ryan J

  • Full Member
  • ***
  • Posts: 141
Re: FPC memory manager vs C std library
« Reply #21 on: August 02, 2023, 07:47:46 pm »
The copying of the VMT is using undocumented assumptions about the format of the VMT, which could change at any fpc update or potentially between different optimization levels.

ah yes that makes sense. Free pascal for some reason keeps the location of the member variables private so you can't do MemMove on a class so I guess the VMT is the same. I don't get that because it would be helpful to be able to copy class members in one step instead of doing all the assignments manually. Maybe using RTTI is how they want us to do it.

PascalDragon

  • Hero Member
  • *****
  • Posts: 6353
  • Compiler Developer
Re: FPC memory manager vs C std library
« Reply #22 on: August 02, 2023, 10:12:05 pm »
The copying of the VMT is using undocumented assumptions about the format of the VMT, which could change at any fpc update or potentially between different optimization levels.

ah yes that makes sense. Free pascal for some reason keeps the location of the member variables private so you can't do MemMove on a class so I guess the VMT is the same. I don't get that because it would be helpful to be able to copy class members in one step instead of doing all the assignments manually. Maybe using RTTI is how they want us to do it.

A simple Move would mess with fields of reference counted types.

You're also not supposed to poke into the internals of a class (both private and protected), especially if it's a third party one.

Thaddy

  • Hero Member
  • *****
  • Posts: 18765
  • To Europe: simply sell USA bonds: dollar collapses
Re: FPC memory manager vs C std library
« Reply #23 on: August 03, 2023, 09:28:33 am »
@Warfley
In the end, you will come to the same conclusion as I did many moons ago: derive from a custom class for stack allocated classes. Much, much, much simpler and it works.

Btw: the system unit contains some gems to play with (just you, not recommended for most others): sptr, stacktop (leave alone) , stackbottom and stacklength. Those can be useful if you want to pursue your efforts.
« Last Edit: August 03, 2023, 09:46:10 am by Thaddy »
If Europe sells their USA bonds the USD will collapse. Europe can affort that given average state debts. The USA can't affort that. Just an advice...

Ryan J

  • Full Member
  • ***
  • Posts: 141
Re: FPC memory manager vs C std library
« Reply #24 on: August 06, 2023, 04:32:47 am »
And the stack allocator is assuming memory layout of the stack which also is not documented an can be subject to things like inclining, exception handling etc. It already breakers in the code above when trying to call the stack function twice. I assume this is because strings add a hidden try-except frame which I break, but to be fully honest, I have no idea why it doesn't work.

And one more thing, why again isn't alloca (https://man7.org/linux/man-pages/man3/alloca.3.html) an option here? Is it even supported with FPC? I know I asked this before somewhere but I've forgotten...

I don't even understand how alloca could work unless it's the compiler that implements it. How can a general library know about the stack anyways? Isn't that the sole responsibility of the compiler? I thought the stack was fixed in the binary also so that's another curious question.

Ryan J

  • Full Member
  • ***
  • Posts: 141
Re: FPC memory manager vs C std library
« Reply #25 on: August 06, 2023, 06:04:19 am »
Never mind on my last question about alloca. I did some searching and found the answers. I found Thaddy's code below and why it's not safe. Also this thread https://forum.lazarus.freepascal.org/index.php/topic,62524.15.html.

I will say I don't understand how that alloca function would work even since it should be allocating inside the constructors stack frame, unless constructors don't have stack frames?? that I'm not sure about still.

Code: Pascal  [Select][+][-]
  1. program stackbasedclasses;
  2. {$ifdef fpc}{$mode delphi}{$H-}{$S+}{$endif}
  3. {$ifndef win64}{$error this is for windows x86_64 only!}{$endif}
  4. {$L allocafunc64.obj}
  5. function _alloca(theSize: integer; alignm: integer; accumPtr: pointer = nil): pointer;
  6.   external Name '_alloca';
  7.  
  8. type
  9.   TStackObject = class
  10.   public
  11.      class function newinstance : tobject;override;
  12.      procedure FreeInstance;override;
  13.      procedure show;virtual;
  14.   end;
  15.  
  16.   TStackObject2 = class(TStackObject)
  17.   public
  18.     procedure Show;override;
  19.   end;
  20.      
  21.   class function TStackObject.NewInstance:Tobject;
  22.   var
  23.     p : pointer;
  24.   begin
  25.     p := _alloca(InstanceSize,4);  // allocate on the stack. 4 byte aligned
  26.     if p <> nil then
  27.       InitInstance(p);
  28.     NewInstance:=TObject(p);
  29.   end;
  30.  
  31.   procedure TStackObject.FreeInstance;
  32.   begin
  33.     // CleanUpInstance; // not nessesary
  34.     // no free!
  35.   end;
  36.  
  37.   procedure TStackObject.Show;
  38.   begin
  39.     writeln(self.Classname);
  40.   end;
  41.  
  42.   procedure TStackObject2.Show;
  43.   begin
  44.     writeln(self.Classname);
  45.   end;
  46.  
  47.  
  48. var
  49.  testme,testme2:TStackObject;  
  50. begin
  51.  testme := TStackObject.Create;
  52.  testme.show;
  53.  testme2 := TStackObject2.Create;
  54.  Testme2.Show;
  55. end.

 

TinyPortal © 2005-2018