Skip to main content
+ test program.
Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.


Edit 1: From the same section of the avr-libc documentation, we learn about how realloc() grows a memory chunk:

If [...] the old chunk is at the top of heap, and the above freelist walk did not reveal a large enough chunk on the freelist to satisfy the new request, an attempt is made to quickly extend this topmost chunk (and thus the heap), so no need arises to copy over the existing data.

Based on this, I would suggest you use an array, rather than a linked list, and use realloc() to adjust the size of the array. A linked list of n objects of size m has a memory cost n × (m+4): each objects has an overhead of 2 bytes for the linking pointer, plus 2 bytes for malloc()'s internal bookkeeping. The cost of an array would be only (n × m)+2.

In this case, however, it is essential that no other part of your code uses malloc(), otherwise realloc() could end up allocating a whole new array, which would be very costly.


Edit 2: I wrote a small test program that randomly grows and shrinks a memory buffer. Running the test on an Uno shows that the allocated memory chunk never changes address even though its size varies wildly.

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static void *p;
    static size_t sz;
    sz = random(max(sz, 22) - 20, min(sz + 20, 1536) + 1);
    p = realloc(p, sz);
    Serial.print((uintptr_t) p);
    Serial.print("  ");
    Serial.println(sz);
}

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.


Edit: From the same section of the avr-libc documentation, we learn about how realloc() grows a memory chunk:

If [...] the old chunk is at the top of heap, and the above freelist walk did not reveal a large enough chunk on the freelist to satisfy the new request, an attempt is made to quickly extend this topmost chunk (and thus the heap), so no need arises to copy over the existing data.

Based on this, I would suggest you use an array, rather than a linked list, and use realloc() to adjust the size of the array. A linked list of n objects of size m has a memory cost n × (m+4): each objects has an overhead of 2 bytes for the linking pointer, plus 2 bytes for malloc()'s internal bookkeeping. The cost of an array would be only (n × m)+2.

In this case, however, it is essential that no other part of your code uses malloc(), otherwise realloc() could end up allocating a whole new array, which would be very costly.

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.


Edit 1: From the same section of the avr-libc documentation, we learn about how realloc() grows a memory chunk:

If [...] the old chunk is at the top of heap, and the above freelist walk did not reveal a large enough chunk on the freelist to satisfy the new request, an attempt is made to quickly extend this topmost chunk (and thus the heap), so no need arises to copy over the existing data.

Based on this, I would suggest you use an array, rather than a linked list, and use realloc() to adjust the size of the array. A linked list of n objects of size m has a memory cost n × (m+4): each objects has an overhead of 2 bytes for the linking pointer, plus 2 bytes for malloc()'s internal bookkeeping. The cost of an array would be only (n × m)+2.

In this case, however, it is essential that no other part of your code uses malloc(), otherwise realloc() could end up allocating a whole new array, which would be very costly.


Edit 2: I wrote a small test program that randomly grows and shrinks a memory buffer. Running the test on an Uno shows that the allocated memory chunk never changes address even though its size varies wildly.

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    static void *p;
    static size_t sz;
    sz = random(max(sz, 22) - 20, min(sz + 20, 1536) + 1);
    p = realloc(p, sz);
    Serial.print((uintptr_t) p);
    Serial.print("  ");
    Serial.println(sz);
}
+ solution with array and realloc()
Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.


Edit: From the same section of the avr-libc documentation, we learn about how realloc() grows a memory chunk:

If [...] the old chunk is at the top of heap, and the above freelist walk did not reveal a large enough chunk on the freelist to satisfy the new request, an attempt is made to quickly extend this topmost chunk (and thus the heap), so no need arises to copy over the existing data.

Based on this, I would suggest you use an array, rather than a linked list, and use realloc() to adjust the size of the array. A linked list of n objects of size m has a memory cost n × (m+4): each objects has an overhead of 2 bytes for the linking pointer, plus 2 bytes for malloc()'s internal bookkeeping. The cost of an array would be only (n × m)+2.

In this case, however, it is essential that no other part of your code uses malloc(), otherwise realloc() could end up allocating a whole new array, which would be very costly.

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.


Edit: From the same section of the avr-libc documentation, we learn about how realloc() grows a memory chunk:

If [...] the old chunk is at the top of heap, and the above freelist walk did not reveal a large enough chunk on the freelist to satisfy the new request, an attempt is made to quickly extend this topmost chunk (and thus the heap), so no need arises to copy over the existing data.

Based on this, I would suggest you use an array, rather than a linked list, and use realloc() to adjust the size of the array. A linked list of n objects of size m has a memory cost n × (m+4): each objects has an overhead of 2 bytes for the linking pointer, plus 2 bytes for malloc()'s internal bookkeeping. The cost of an array would be only (n × m)+2.

In this case, however, it is essential that no other part of your code uses malloc(), otherwise realloc() could end up allocating a whole new array, which would be very costly.

Source Link
Edgar Bonet
  • 45.2k
  • 4
  • 42
  • 81

On my answer to the question you are referencing, I state that “If you use the heap like a stack (last in is first out), then it will behave like a stack and not fragment.” This seems to be exactly your usage pattern, thus you should not fear fragmentation. Of course, the caveat written in Michel Keijzers’ answer applies: you should make sure that no other part of your code, including libraries, is using malloc() and free().

Just to give some evidence, the avr-lic documentation provides some implementation details about malloc:

When allocating memory [...] If nothing could be found on the freelist, heap extension is attempted. [...] When deallocating the topmost chunk of memory, the size of the heap is reduced.

All this can be seen in action in the source code of malloc() and free(), which is abundantly commented and quite readable.