Forum > Beginners

Memory layout and management questions

<< < (2/3) > >>

K.IO.s:
Thanks once more, last reply cleared a few more things.

With regards to the MemoryManager, i was referring to this guy:
https://www.freepascal.org/docs-html/prog/progsu174.html

It seems to be the paradigm I'm looking for. Currently I'm thinking that at the beginning of the Program i can get the standard MemMan, save it in a global, main variable.
Then upon arriving at my unit, or in a routine that I believe requires a bump allocator, I'll set the bumpManager, and at the end of the routine set back the main one back.

The real problem will come with multiple threads. I'm guessing SetMemoryManager isn't thread local or unit scoped. So i can have two different implementations alternating around which would be ugly. In single threaded, with a predictable control flow, it should be less of a problem.

Ideally, as with pmr allocators(in a senseĀ¹), the MemoryManager(the obj) in use should be fully localized, or some option in that regard should be there. It's just calls to the heap anyways.
Specially for people who do a lot of manual memory management this type of thing can greatly streamline the process.

If you have any other hints to add let me know.

NoteĀ¹:
For people who aren't aware, in c++17 types where extended so that you can pass an allocator to it, and therefore control the allocation strategy of each type, and section of the code more closely and easily. In games, databases and other high performance software that's useful.
The current freePascal solution with the MemoryManager obj doesn't seem to be as fine grained in that it seems to set an object of global usage. It's the closest thing I've found.

440bx:

--- Quote from: K.IO.s on July 06, 2022, 03:28:43 am ---Thanks once more, last reply cleared a few more things.

--- End quote ---
You're welcome.


--- Quote from: K.IO.s on July 06, 2022, 03:28:43 am ---With regards to the MemoryManager, i was referring to this guy:
https://www.freepascal.org/docs-html/prog/progsu174.html

--- End quote ---
I take that as meaning you'd like to have some assistance from the compiler to simplify/streamline memory management for you.  That's quite reasonable, it's what the majority of programmers go for but, there is a price to pay for that convenience.  (more on that follows.)


--- Quote from: K.IO.s on July 06, 2022, 03:28:43 am ---The real problem will come with multiple threads.

--- End quote ---
And it can easily become a problem in a single threaded program.  The real problem is that, in any somewhat complex program, even a single thread one, depending on what some parts of the code need, memory should be managed a certain way.  This leads to the optimal ways being different for different parts of the code.  Just about any method that attempts to cater to all different needs is going to fall short in one way or another.


--- Quote from: K.IO.s on July 06, 2022, 03:28:43 am ---If you have any other hints to add let me know.

--- End quote ---
I'll tell you what I do but, you'll need to "customize" it for your program's needs.  First, I rarely allocate heap memory.  If a heap is needed by some part of the program, that part of the program is then responsible for creating its own heap (HeapCreate in Window) and destroying it when it is no longer needed.  That way, no critical sections are needed to read/write the heap because the heap is private to that piece of code.  Very often, there is no need to individually free blocks, instead everything is freed at once when the heap is destroyed.  No memory leaks that way.

More often than not, my programs have a number of blocks of VirtualAlloc-ed memory blocks each of which is written to by, ideally, only one easily identifiable block of code.  Also, quite often a program needs to make some data available, for reading only, to other parts of the program. That's another block (the program's core constant data) and, once setup, it becomes _read_only_ (set it up and freeze it, VirtualProtect it in Windows.) If "foreign" parts of the program need to write to it (something to be avoided but sometimes either unavoidable or avoiding it would significantly complicate the code) then that part of the code (usually a unit) provides specific interfaces to carry out those specific actions (nothing general, everything very specific, to prevent undesirable side-effects.)

The above makes it obvious that I don't rely in any way on the compiler to do memory management.  It's more work on my part but, the performance, maintainability and debug-ability of the code is vastly better than what could be had using the compiler's facilities (and by compiler, in this case, I mean, _any_ compiler, not just FPC.)

I realize that most programmers don't want to go through that much additional work and will choose to have the assistance of the compiler.

MarkMLl:

--- Quote from: K.IO.s on July 06, 2022, 01:10:06 am ---For the sake of reference, here is one example of posts that I saw that seemed to indicate arrays were not contiguous in freePascal: https://forum.lazarus.freepascal.org/index.php/topic,39739.msg273701.html#msg273701

And here for an example saying arrays are auto allocated on the heap:
https://forum.lazarus.freepascal.org/index.php/topic,34989.msg230535.html#msg230535

There were others where people explicitly stated such things. That's what got me confused in the first place. Why would the language have such obscure design choices.

--- End quote ---

Those are specifically referring to dynamic arrays of two or more dimensions.

The compiler and runtimes handles strings and arrays where the size is not specified in the declaration, as well as instantiated classes, specially: a small block (call it a descriptor if you insist) is inserted at the expected place in the stack or global memory which should by and large be treated as an opaque structure with pointers to the heap. The detail of this is hidden, there is no need to use a pointer-dereference operator (^).

In the case of strings (without a declared length) and dynamic arrays (i.e. without a declared length), there is an implicit finalisation block in the local function which reference-counts the allocation and frees the heap storage when appropriate. The result of this is that strings and dynamic arrays may be safely returned as function results.

If the data associated with a string or a single-dimensioned dynamic array is to be passed to e.g. an OS-level API, then it must be assumed to start at an indexed element, i.e. somestring[ 1] or somearray[ 0]. I believe it is reasonable for data to be assumed to be contiguous, it might not be safe to assume it is safe from relocation.

However, a dynamic array containing a further level of dynamic arrays or strings will actually contain opaque control blocks, which themselves point into the heap. The complexity of dereferening, destruction and where necessary reallocation is (normally) hidden from the user.

MarkMLl

PascalDragon:

--- Quote from: 440bx on July 06, 2022, 12:13:45 am ---
--- Quote from: K.IO.s on July 05, 2022, 10:45:42 pm ---...it seems that they don't have a contiguous memory layout, and thus the data is allocated all over the place with pointers everywhere.

--- End quote ---
No, the arrays the compiler builds always have contiguous elements, the compiler depends on that to access the array elements.  IOW, allocation isn't all over the place, linearity is always preserved.
--- End quote ---

The question was in the context of array of array of Type. In this case each element of the outer array is in fact a pointer to another dynamic array (and each sub array can have a different length). This is different from a static multidimensional array which is indeed contiguous.

440bx:

--- Quote from: PascalDragon on July 06, 2022, 01:37:06 pm ---The question was in the context of array of array of Type. In this case each element of the outer array is in fact a pointer to another dynamic array (and each sub array can have a different length). This is different from a static multidimensional array which is indeed contiguous.

--- End quote ---
Yes, you are right.  Each array is dynamically dimensioned individually which means they each are in separate dynamically allocated blocks and each array can have a different number of elements.

Thank you for making it clear.  I'm sure the OP appreciates it too.


Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version