News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Memory allocation best-practices

Started by redskull, December 26, 2007, 03:02:47 AM

Previous topic - Next topic

redskull

To make a broad generalization, there seems to be two ways of dealing with procedures that work on substantial amounts of memory:  1) allocate the memory (whichever way) in the calling code, and pass a pointer as an argument to the procedure to have it 'fill' it, or 2) Allocate the memory dynamically within the procedure, and pass back a pointer to the calling code as a return value (making sure to remember to deallocate it later).  To me, one way seems as good as the other, but I was wondering if anyone with experience could share words of advice about the pros/cons of either (not really from a performance standpoint, but more from a modular design view).
TIA
-alan
Strange women, lying in ponds, distributing swords, is no basis for a system of government

sinsi

Well, Windows itself usually requires the caller to allocate the memory, but sometimes (e.g. FormatMessage) it can allocate memory for you.

Myself, I prefer the caller to allocate the memory (since it's within the proc - alloc/call/massage/free), and it is easier to respond to an error, since your sub-proc should return a pointer and an error code.
Light travels faster than sound, that's why some people seem bright until you hear them.

Mirno

It should come down to independance.
If the procedure creates the memory, that ties both the proc and the caller to a specific memory allocation routine.
If the procedure is passed some memory, then the procedure is more portable.

Imagine for a second that you port your app to Windows Imaginary edition 2100. The VirtualAlloc you've been using has been depreciated, and you move to the new SuperMemoryAlloc. If you still want to support XP too, then you need two versions of the proc, if the proc is responsible for memory allocation.

Unless there is a requirement for an unknown amount of memory, then it's better for the caller to allocate and deal with the memory.

Just my opinion.

Mirno

Ehtyar

Quote from: Mirno on December 27, 2007, 02:57:38 PM
It should come down to independance.
If the procedure creates the memory, that ties both the proc and the caller to a specific memory allocation routine.
If the procedure is passed some memory, then the procedure is more portable.

Imagine for a second that you port your app to Windows Imaginary edition 2100. The VirtualAlloc you've been using has been depreciated, and you move to the new SuperMemoryAlloc. If you still want to support XP too, then you need two versions of the proc, if the proc is responsible for memory allocation.

Unless there is a requirement for an unknown amount of memory, then it's better for the caller to allocate and deal with the memory.

Just my opinion.

Mirno
You're still required to free the memory after use, and there is no one all-powerful free function that can be used for each type of allocation.

Ehtyar.

donkey

I agree 100% with Mirno, the caller should almost always be responsible for allocating memory, it is good coding practice and as he said it makes sense for compatibility reasons. Where do I get an advance copy of Windows Imaginary edition 2100 ? Hopefully Microsoft and His Royal Highness B. Gates XVII has patched a couple of security leaks with the quantum computing interface and fixed the IDeath2Humans interface.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

hutch--

I agree with the general idea of passing memory rather than building a particular memory strategy directly into a procedure but this assumes that you already know how much memory to allocate and this is not always the case. Even if you pass a memory handle, if the buffer is not big enough you must REALLOC the memory and this again is strategy specific in the API memory allocation strategies, for every ALLOC there is a REALLOC and a FREE so I don't think you can escape the problem in some instances.

I have a number of algos in the masm32 library that cannot be done any other way so I have intentionally used the depricated GlobalAlloc() with the fixed memory flag because the API is maintained for other legacy reasons and in fact all it does is call the same routine in NTDLL.DLL that most of the others use. I would still finally prefer to allocate a single block and chop it up in whatever way I like than use the later non-fragmenting strategies that tend to be much slower.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

donkey

Both HeapAlloc and GlobalAlloc allocate memory from the heap (though the wrapped API calls through NTDLL make GlobalAlloc slower) and can lead to heap fragmentation for large amounts of memory, slowing heap access and therefor potentially your program. The VirtualAlloc function is by far the best choice for large allocations and is as good as any other for small ones but does not cause any fragmentation. For many of the Win16 legacies, such as DDE and the clipboard GlobalAlloc is the only choice because of Microsoft's obsession with backward compatibility.

For myself I use what ever I feel like at the moment, I don't pay much attention to which allocation function I use, but I do know which one is best for general purpose memory allocation, namely VirtualAlloc.

Donkey
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

u

Both cases are used in their appropriate places. Neither is universal.

You use case 1) when the array-size depended on something external, which your called procedure needn't care about. [Caller cares about size, callee doesn't]. You see this case in a general procedural-style implementation.

You use case 2) when the array-size depends on something internal to the called procedure. [Caller doesn't care about size, callee does]. You see this case in an OOP class' implementation.

There is also case 3), where the caller first asks that OOP object about memory-size, then allocates the memory and finally calls 1). But I personally don't like such exchange of assignments between modules [breaks modularity, begs for bugs, begs for instability in multithreading, or simply complicates things a bit too much] . There _are_ examples of case 3)  being very useful, you just have to be entirely confident in the all code in the path and about the environment it's being run in.
Please use a smaller graphic in your signature.