Thread overview | |||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
January 30, 2021 Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Is there a way to force the GC to re-use memory in already existing pools? I set maxPoolSize:1 to gain pools that can be quicker released after there no longer in use. This already reduces memory usage to 1:3. Sadly the application creates multiple pools that are not necessary in my POV - just fragmented temporary slice data like from format(). What can I do to optimize? |
January 30, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame | On Saturday, 30 January 2021 at 16:42:35 UTC, frame wrote:
> Is there a way to force the GC to re-use memory in already existing pools?
>
> I set maxPoolSize:1 to gain pools that can be quicker released after there no longer in use. This already reduces memory usage to 1:3. Sadly the application creates multiple pools that are not necessary in my POV - just fragmented temporary slice data like from format(). What can I do to optimize?
Do you want to optimize for reduced memory usage?
|
January 31, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to Imperatorn | On Saturday, 30 January 2021 at 22:57:41 UTC, Imperatorn wrote:
> On Saturday, 30 January 2021 at 16:42:35 UTC, frame wrote:
>> Is there a way to force the GC to re-use memory in already existing pools?
>>
>> I set maxPoolSize:1 to gain pools that can be quicker released after there no longer in use. This already reduces memory usage to 1:3. Sadly the application creates multiple pools that are not necessary in my POV - just fragmented temporary slice data like from format(). What can I do to optimize?
>
> Do you want to optimize for reduced memory usage?
Yes, speed is secondary (long running daemon)
|
January 31, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame | On Saturday, 30 January 2021 at 16:42:35 UTC, frame wrote:
> Is there a way to force the GC to re-use memory in already existing pools?
>
> I set maxPoolSize:1 to gain pools that can be quicker released after there no longer in use. This already reduces memory usage to 1:3. Sadly the application creates multiple pools that are not necessary in my POV - just fragmented temporary slice data like from format(). What can I do to optimize?
I can't tell you much about the inner workings of the GC, but maybe take a look at experimental.allocator and see if anything there can help you (specifically that you understand your program better than the GC)
|
January 31, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame | On Sunday, 31 January 2021 at 04:12:14 UTC, frame wrote: > On Saturday, 30 January 2021 at 22:57:41 UTC, Imperatorn wrote: >> On Saturday, 30 January 2021 at 16:42:35 UTC, frame wrote: >>> Is there a way to force the GC to re-use memory in already existing pools? >>> >>> I set maxPoolSize:1 to gain pools that can be quicker released after there no longer in use. This already reduces memory usage to 1:3. Sadly the application creates multiple pools that are not necessary in my POV - just fragmented temporary slice data like from format(). What can I do to optimize? >> >> Do you want to optimize for reduced memory usage? > > Yes, speed is secondary (long running daemon) It says experimental, but it's fine: https://dlang.org/phobos/std_experimental_allocator.html |
February 03, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to Imperatorn | On Sunday, 31 January 2021 at 12:14:53 UTC, Imperatorn wrote: > It says experimental, but it's fine: > > https://dlang.org/phobos/std_experimental_allocator.html Well, this looks very nice but I have to deal with GC as long I want to use other libraries that are relying on it or even just phobos. Conclusion so far (for Windows): 32bit: - GC just doesn't work at all 64bit: - Collections are rare. It can be necessary to call GC.collect() manually. - Scope guards to explicit clean up / free memory at function exit have no deep impact on most cases. - If your application should save memory call GC.minimize() when it's appropriate. It seems that calling GC.enable() if it's already enabled just disables the automatic GC again? Is this a bug? I cannot reproduce it outside my application yet since it's not clear when the GC starts collecting, but it always shows the same behaviour: > // GC.enable(); Case A: The app is very kind in memory usage (~20 MB) > GC.enable(); Case B: The app is consuming huge amount of memory (~900 MB) > GC.disable(); > GC.enable(); Case A again > GC.disable(); > GC.enable(); > GC.enable(); Case B again I also have to struggle what the specs' text actually mean: > This function is reentrant, and must be called once for every call to disable before automatic collections are enabled. |
February 05, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame | On Wednesday, 3 February 2021 at 13:37:42 UTC, frame wrote: > I have to deal with GC as long I want to use other libraries that are relying on it or even just phobos. > > Conclusion so far (for Windows): > > 32bit: > - GC just doesn't work at all ?? Do you mean no collections happen? 32bit GC should just work. > 64bit: > - Collections are rare. It can be necessary to call GC.collect() manually. > - Scope guards to explicit clean up / free memory at function exit have no deep impact on most cases. > - If your application should save memory call GC.minimize() when it's appropriate. > > > It seems that calling GC.enable() if it's already enabled just disables the automatic GC again? Is this a bug? I cannot reproduce it outside my application yet since it's not clear when the GC starts collecting, but it always shows the same behaviour: > >> // GC.enable(); > Case A: The app is very kind in memory usage (~20 MB) > >> GC.enable(); > Case B: The app is consuming huge amount of memory (~900 MB) > >> GC.disable(); >> GC.enable(); > Case A again > >> GC.disable(); >> GC.enable(); >> GC.enable(); > Case B again That looks like a bug indeed. > I also have to struggle what the specs' text actually mean: > >> This function is reentrant, and must be called once for every call to disable before automatic collections are enabled. I think it means that you need to make sure that enable() is called as many times as disable() is called before collection can happen automatically. — Bastiaan. |
February 06, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bastiaan Veelo | On Friday, 5 February 2021 at 22:46:05 UTC, Bastiaan Veelo wrote: > I think it means that you need to make sure that enable() is called as many times as disable() is called before collection can happen automatically. > > — Bastiaan. Thanks, in the meanwhile I looked into the source: > struct Gcx > { > uint disabled; // turn off collections if >0 > ... > } > void enable() > { > static void go(Gcx* gcx) nothrow > { > assert(gcx.disabled > 0); > gcx.disabled--; > } > runLocked!(go, otherTime, numOthers)(gcx); > } > void disable() > { > static void go(Gcx* gcx) nothrow > { > gcx.disabled++; > } > runLocked!(go, otherTime, numOthers)(gcx); > } So that explains what's going on. The assertion should kick in to warn about this issue. But it doesn't work on user code. I assume the runtime is not compiled but just linked or do I need another argument switch? |
February 06, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to Bastiaan Veelo | On Friday, 5 February 2021 at 22:46:05 UTC, Bastiaan Veelo wrote: > ?? Do you mean no collections happen? 32bit GC should just work. No, it doesn't - this code fails on memory allocation and works fine with -m64 switch: import std.stdio; import core.memory : GC; void main() { void usage() { writefln("Usage: %.2f MiB / collected: %d", (cast(double) GC.stats.usedSize) / 1_048_576, GC.profileStats.numCollections); } void foo() { string[] s; scope (exit) { s.length = 0; } foreach (i; 0 .. 50_000_00) { s ~= "a"; } } foreach (i; 0 .. uint.max) { writefln("Round: %d", i + 1); foo(); GC.collect(); usage(); } } ... Round: 24 Usage: 1603.57 MiB / collected: 27 Round: 25 Usage: 1691.64 MiB / collected: 28 Round: 26 Usage: 1729.50 MiB / collected: 29 Round: 27 core.exception.OutOfMemoryError@src\core\exception.d(647): Memory allocation failed |
February 06, 2021 Re: Minimize GC memory footprint | ||||
---|---|---|---|---|
| ||||
Posted in reply to frame | On 06/02/2021 3:32 PM, frame wrote: > On Friday, 5 February 2021 at 22:46:05 UTC, Bastiaan Veelo wrote: > >> ?? Do you mean no collections happen? 32bit GC should just work. > > No, it doesn't - this code fails on memory allocation and works fine with -m64 switch: > > > import std.stdio; > import core.memory : GC; > > void main() { > > void usage() { > writefln("Usage: %.2f MiB / collected: %d", (cast(double) GC.stats.usedSize) / 1_048_576, GC.profileStats.numCollections); > } > > void foo() { > string[] s; > > scope (exit) { > s.length = 0; This won't do anything. > } > > foreach (i; 0 .. 50_000_00) { > s ~= "a"; > } > } > > foreach (i; 0 .. uint.max) { > writefln("Round: %d", i + 1); Don't forget to stdout.flush; Otherwise stuff can get caught in the buffer before erroring out. > foo(); > GC.collect(); > usage(); > } > } > > ... > Round: 24 > Usage: 1603.57 MiB / collected: 27 > Round: 25 > Usage: 1691.64 MiB / collected: 28 > Round: 26 > Usage: 1729.50 MiB / collected: 29 > Round: 27 > > core.exception.OutOfMemoryError@src\core\exception.d(647): Memory allocation failed Turn on the precise GC, 32bit is a bit too small of a range and you can get false positives like in this case (at least looks like it). |
Copyright © 1999-2021 by the D Language Foundation