0

I'm running pipeline (thread's pipeline from OmniThreadLibrary) from another thread and got memory leak or rather memory consumption. But when application close then it's ok and there are no memory leak report (ReportMemoryLeaksOnShutdown := True;).

Here example: click button 10 times and test app will get ~600 MB of memory. Windows 7 x64, Delphi XE6, latest omni source.

It's a bug? Or I need use another code?

uses
  OtlParallel,
  OtlCommon;

procedure TForm75.Button1Click(Sender: TObject);
begin
  // run empty pipeline from another threads
  Parallel.&For(1, 100).Execute(
    procedure(value: integer)
    var
      pipe: IOmniPipeline;
    begin
      pipe := Parallel.Pipeline
        .Stage(procedure(const input: TOmniValue; var output: TOmniValue) begin end)
        .Run;
      pipe.Cancel;
      pipe.WaitFor(100000);
      pipe := nil;
    end
  );
end;

Edit 1: Tested that code with ProcessExplorer and found what threads count at runtime is constant, but handles count is grown. If I'm insert Application.ProcessMessages; at the end of "for loop" (after pipe's code) then test app running good, handles are closing and memory consumption is constant. Don't know why.

JayDi
  • 1,037
  • 15
  • 24

1 Answers1

0

How many threads does it create ?

Check it in SysInternals Process Explorer for example. Or in Delphi IDE (View -> Debug Windows -> Threads)

I think that because you block each For-worker for wuite a long WaitFor your application then creates many worker threads for every button click, and when you click it 10 times it consequently creates 10 times many threads.

And yes, in general-purpose operating systems like Windows threads are expensive! Google for "windows thread memory footprint" - and multiply it by the number of threads created by 10 parallel-for loop you spawn.

This fact was the reason that for making highly parallel server applications special approaches were done to create light-eight application-level threads and bypass OS threads, to name a few

However OTL being a generic library for generic threads imposes little restrictions but relies on OS-provided native threads, and they are heavy expensive in both CPU time needed to create/release Windows Threads (mitigated by Thread Pools concept) and by memory footprint needed to maintain each Windows Threads by OS (which is unavoidable and you see its manifestation).

Of course later, when all those loops are worked through, their threads are getting closed and released, together with the memory that was used to maintain them. So no memory leak indeed, once you wait enough for all your threads to be terminated - they are, with all the temporarily allocated memory they used as their workplaces.

UPD. How to check that hypothesis? easiest way would be to change how many threads is spawned by every instance of For-Loop (by every button click).

See the .NumTasks options of you Parallel.For object:

http://otl.17slon.com/book/chap04.html#leanpub-auto-iomniparallelsimpleloop-interface

By default every button click should spawn one thread for every CPU core. But you can enforce your own size of thread pool. Add .NumTasks(1) call and check memory consumption, then check it into .NumTasks(10) and do it again. If the memory consumption would grow approximately tenfold after that - then it is it.

Community
  • 1
  • 1
Arioch 'The
  • 15,799
  • 35
  • 62
  • Testing with ProcessExplorer -- threads count on runtime is constant. Problem was in "handlers" -- it's grow and not close. After insert `Application.ProcessMessages;` at the end of "for loop" -- test app work good -- handlers are closing and memory consumption is not grow. Don't known why. – JayDi Apr 20 '16 at 07:25
  • So i think thread objects count is growing (handles) but active workers (running threads in PE) is constant (balanced by cpu cores count). Yesterday i saw there is a similar q on SO about console applications and otl, where memory is not released - because OTL is written over windows messages and needs them for housekeeping. I am at phone now and can't find the link right off. – Arioch 'The Apr 20 '16 at 08:19
  • Did you tried comparing footprints with different .NumTasks() values? Also the Loop should have a flag like .NoWait() offloading all the workers into out-of-GUI threads thus freeing GUI thread to process housekeeping messages. Try it too please. – Arioch 'The Apr 20 '16 at 08:22
  • BTW each thread has to create an invisible window for communication and management. API call like AllocateHWND or AllocateWindowHandle.... That way not released thread inactive objects would indeed manifest themselves as Windows handles leak – Arioch 'The Apr 20 '16 at 08:37