AsyncWebServer Library - heap memory use

I am serving a web page that is about 130k in size. I am storing the web page in SDRAM to save RAM.

This works well, the web page is intact, and not corrupted.

When serving the root page for the first time, I get these heap and stack statistics

HEAP DATA - In Async request freemem=69633 Cur heap: 75207 Res Size: 436408 Max heap: 75712
STACK
Thread: 0x2400f728, Stack size: 1200, Max stack: 1200
Thread: 0x24003094, Stack size: 5000, Max stack: 1408

HEAP DATA - Post Async request freemem=76793 Cur heap: 180534 Res Size: 436408 Max heap: 393933
STACK
Thread: 0x2400f728, Stack size: 1200, Max stack: 1200
Thread: 0x24003094, Stack size: 5000, Max stack: 1408

(I eliminated other threads from the display - there are others that are not pertinent)

You can see that the heap size grows quite large after the web page is served, which is an issue, as I think it is over running my stacks (there is another thread ? LWIP_TCPIP_THREAD that hits its stack limit - asked in mbed forum if this is normal).

Is there any way to make AsyncWebServer little less of a heap hog, and serve the web page from the SDRAM source, as was my intention?

Thanks for any suggestions

I cannot help you directly but for my project, I have avoided using AsyncWebServer after reading the source code. The heavy usage of the dynamic allocation scared me (maybe unfounded). I am scared of memory fragmentation in the long term.
For my simple application, I have used the default Arduino Ethernet Server. It allocates memory for each new client but after more than 50 000 connection/disconnection, the heap was still in the same state but I have carefully avoided any allocation elsewhere.

About the AsyncWebServer, I have seen that @khoih-prog has archived all its network related Portenta H7 repositories so you would have to make you own fork to apply some changes to it if needed.

Well, for now, I moved all the favicons, as well as my image logo to an azure CDN. This has cut the web page from 130k to something like 60-70k. It has given me some breathing room for now.

Some web activities - such as creating agraph from 1 yr long stored data could take 10sec +, and the Async library should have the advantage of not locking up I believe when one of those requests comes in. This is why I preferred using it in my case.

Another problem is that ajax query responses are also using heap. Passing a char * array (malloc'ed in SDRAM) of sensor data results in 3x the array size in heap requirements - as the variable is turned into a string, and then there are string concatenations that are occurring.

I created an issue in the portenta asyncTCP github and posted some example code demonstrating the issue - we'll see if it gets taken up.

I created a new mod to the standard library, and is currently considered for merging with the main code.

If interested it can be accessed here

RSMOD

In my code, my max heap size went from 374k to approx 68k, and adding more sensors, or larger sends will not affect the heap, just use up more SDRAM (that is where I save my C strings to send as ajax responses, as well as the actual web page to send).

In the examples included in the repository, the heap use went from 111k to 12k.

1 Like

Hi @rs77can

The new Portenta_H7_AsyncWebServer v1.4.0 has been published, thanks to your marvellous idea and PR

Looking forward to receiving more of your PRs for many other similar AsyncWebServer


Releases v1.4.0

  1. Support using CString in optional SDRAM to save heap to send very large data. Check request->send(200, textPlainStr, jsonChartDataCharStr); - Without using String Class - to save heap #8
  2. Add multiple examples to demo the new feature

It is important to note that the string that is passed may be altered using this software call.

This does not always happen, and I think should only happen if the header is larger than the first packet sent (the remaining packet is then prepended to the original string, and the original string is memmoved tomake space).

Because of this, the passed string should be allocated with enough space to fit the added text.

If the string is to be used again, rather than recreating it, I check if the first 10-20 characters have changed, if not, the string is still good, otherwise - you have to recreate it.

Working in SDRAM however, there is a lot of available space and that should not be an issue either way (ie - keeping a backup up copy for the string).

In my application, I keep a backup copy of the main html string, but sensor data (that could be 1Mb long) are JSONed from SD logged data, and discarded after the send - so - the mod is ideal for this use case.

1 Like

I created a new version that does not destroy the C string. It is being evaluated by khoih-prog. It mostly works by completing the sending of the header first (if the header is larger than a single packet), and then completing the main content send.

If the header is smaller than a packet there should be little difference from before (as that was never destructive to the C string anyways).

The only negative is that an extra ethernet packet may be required to complete the send.

On the big plus side, you no longer have to care about having extra space in the passed C string, as it will never be memmove'd or extended in anyway. Should help decrease the chance of stealth bugs in the code from happening.

Also as before the heap growth is kept to a minimum, about 1 ethernet packet length maximum.

1 Like

Again, thanks to @rs77can 's PRs, the new release has been published.

The great works of @rs77can has been merged into many AsyncWebServer libraries, as follows

  1. AsyncWebServer_RP2040W
  2. AsyncWebServer_WT32_ETH01
  3. AsyncWebServer_Ethernet
  4. AsyncWebServer_Teensy41
  5. AsyncWebServer_STM32

and some more to be added


Portenta_H7_AsyncWebServer releases v1.4.1

  1. Don't need memmove(), CString no longer destroyed. Check All memmove() removed - string no longer destroyed #11

hi

is it possible to use it with arduino GIGA?