Why Zephyr porting uses malloc instead of k_malloc?

Hello,
I noticed that Edge Impulse Zephyr porting (edge-impulse-sdk\porting\zephyr\ei_classifier_porting.cpp) uses std malloc instead of Zephyr k_malloc. Does anyone know why? Everything is working fine, I’m just curious.
Methods are weak so I can easily override them, I just don’t know which one I should use.

It is also missing new/delete and new[]/delete[] overloads. Not sure if that’s oversight, or by design.

attribute((weak)) void *ei_malloc(size_t size) {
return malloc(size);
}
attribute((weak)) void *ei_calloc(size_t nitems, size_t size) {
return calloc(nitems, size);
}
attribute((weak)) void ei_free(void *ptr) {
free(ptr);
}

Memory report when running default Edge impulse:

(code for this output at the end of the message)
— Zephyr Kernel Heap (k_malloc) —
Free: 47156 bytes
Allocated: 0 bytes
Peak: 0 bytes

— Libc Heap (malloc) —
Arena Size: 9688 bytes
In-use: 3520 bytes
Free: 6168 bytes

I tested using zephyr malloc, and inference+dsp time is exactly the same. Which one should I use?

attribute((weak)) void *ei_malloc(size_t size) {
return k_malloc(size);
}
attribute((weak)) void *ei_calloc(size_t nitems, size_t size) {
return k_calloc(nitems, size);
}
attribute((weak)) void ei_free(void *ptr) {
k_free(ptr);
}

Memory report when running k_malloc Edge impulse:

(code for this output at the end of the message)
— Zephyr Kernel Heap (k_malloc) —
Free: 43620 bytes
Allocated: 3512 bytes
Peak: 9200 bytes

— Libc Heap (malloc) —
Arena Size: 36 bytes
In-use: 0 bytes
Free: 36 bytes

Memory report code:

#include <zephyr/sys/sys_heap.h>
#include <malloc.h>

extern struct sys_heap _system_heap;

void monitor_memory_usage(void) {
// 1. Monitor k_malloc (Kernel Heap)
struct sys_memory_stats k_stats;
sys_heap_runtime_stats_get(&_system_heap, &k_stats);

printk("--- Zephyr Kernel Heap (k_malloc) ---\n");
printk("Free:      %u bytes\n", k_stats.free_bytes);
printk("Allocated: %u bytes\n", k_stats.allocated_bytes);
printk("Peak:      %u bytes\n\n", k_stats.max_allocated_bytes);

// 2. Monitor malloc (Libc Heap)
// mallinfo is the standard way to check Newlib usage
struct mallinfo m_stats = mallinfo();

printk("--- Libc Heap (malloc) ---\n");
// 'arena' is the total pool size currently held by malloc
// 'uordblks' is the space currently used by the application
printk("Arena Size: %d bytes\n", m_stats.arena);
printk("In-use:     %d bytes\n", m_stats.uordblks);
printk("Free:       %d bytes\n", m_stats.fordblks);
printk("-------------------------------------\n");

}

Hi @tuoman

Thank you for your report and findings.
To be honest, I’m not sure why :slight_smile: , the first Zephyr porting was done time ago and as it is stable, has not been modified, the function is also defined as weak, the user can override it if needed.
I would say it really depends on the conf of your project, which HEAP has been enabled, I’m testing different approach for the heap, as Zephyr (and depends on the target too) offers different solution, probably the best option would be to have a CONFIG_ to let the user choose which one should be used and this is automatically applied to the porting layer, if malloc or k_malloc.

I’ll check how we can improve here, as could lead to headache if not handled carefully.

regards,
fv

1 Like