Reduce program memory size when using "new" functionality to create an instance of a class

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

TL;DR:

 

I want to use the "new" functionality to create an instance of a class, but a lot of extra libraries are loaded so it won't fit my target. Is there a way to reduce the used memory? 

 

Long story:

 

I have a question about memory usage on SAMD21 devices. I have two targets; a development board and a custom designed PCB with almost the same microcontroller:

 

Development board (SAMD21 Xplained Pro)
Microcontroller: ATSAMD21J18
Program memory: 256 KB
RAM: 32 KB

 

Custom designed PCB
Microcontroller: ATSAMD21G16
Program memory: 64 KB
RAM: 8 KB

 

Basically the microcontroller on the boards is the same, but the development board has 4 times as much program memory and RAM. The code I am using is working on the development board, but the custom designed PCB with the other microcontroller does not have enough memory...

 

The code works like this; I have two classes, ClassOne and ClassTwo, which are derived from CommonClass. Depending on whether a digital pin is high, I want either to load an instance of ClassOne or ClassTwo. So directly after some system initialization in my main() loop, I do this:

CommonClass *Object;

if (port_pin_get_input_level(PIN_PA15) == true) { // If switch is pressed
  Object = new ClassOne();
} else {
  Object = new ClassTwo();
}

I never actually delete the object, since it is only created once and lives until power drops or when a reset is done.

 

Before using this approach, I just had two separate *.hex files that made either a ClassOne object or ClassTwo object globally. So it was impossible to change from One to Two dynamically. Choosing the right class dynamically on startup has the advantage of just having one *.hex file that can be uploaded to all my targets.

 

Using MapViewer, I was able to see the difference between the two approaches:

 

1. New approach with the two dynamic objects:

 

A screenshot of MapViewer showing the memory usage with the new approach.

 

2. Old approach, creating an instance of just ClassOne globally:

 

A screenshot of MapViewer showing the memory usage with the old approach.

 

Is there any hope to reduce the project's size?

This topic has a solution.

Maybe someday I'll have a nice quote here.

Last Edited: Tue. Aug 27, 2019 - 08:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Consider using "placement new" and a buffer sized >= the larger of sizeof(ClassOne) and sizeof(ClassTwo).

/Lars

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Lars, thanks for your reply.

 

Does this "placement new" also save program memory? Or only RAM (when done efficiently)?

 

Maybe I was not clear on this but I mostly need to save program memory. This because of all the extra loaded libraries that are needed for the "new" functionality that were not needed before.

Maybe someday I'll have a nice quote here.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you already using LTO and -gc-sections?

I find this a rather endemic problem with ARM chips. “They” have made some nice, cheap Chips with “8bit cpu like resources, but the standard libraries for everything stilll assume “bigger” chips...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Also, try using the “nm -S —size-sort foo.elf” to identify particularly large functions, and/or functions you don’t think need to be there...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear westfw, thanks for your replies.

 

I am not sure what you mean by LTO and -gc-sections. Also I am not sure how to use “nm -S —size-sort foo.elf” as you told me. I will try to Google this now.

 

For reference, I add my current compiler and optimization settings here:

 

ARM/GNU C++ Compiler:

 

Command:

arm-none-eabi-g++

 

All options:

-mthumb -D__SAMD21G16B__ -DF_CPU=48000000UL -DBOARD=USER_BOARD -D__SAMD21G16B__ -DARM_MATH_CM0PLUS=true -DSYSTICK_MODE -DUSART_CALLBACK_MODE=true -DADC_CALLBACK_MODE=false -DTC_ASYNC=true -DTCC_ASYNC=false -DWDT_CALLBACK_MODE=true -DI2S_CALLBACK_MODE=false -DCONFIG_HERO_RELEASE -DI2S_CALLBACK_MODE=true -U__SAMD21J18A__  -Os -ffunction-sections -fno-rtti -fno-exceptions -mlong-calls -g3 -Wall -mcpu=cortex-m0plus -c -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"

Please note that I removed the project specific (irrelevant) stuff from the above options.

 

A screenshot of the compiler optimizations.

 

ARM/GNU Linker:

 

Command:

arm-none-eabi-g++

 

All options:

-mthumb -Wl,-Map="$(OutputFileName).map" -Wl,--start-group -larm_cortexM0l_math -lm  -Wl,--end-group -L"../src/ASF/thirdparty/CMSIS/Lib/GCC"  -Wl,--gc-sections -mcpu=cortex-m0plus -T../src/ASF/sam0/utils/linker_scripts/samd21/gcc/samd21g16b_flash.ld -Xlinker --defsym=STACK_SIZE=0x400

 

A screenshot of the linker optimizations.

 

Can you tell if I am doing something out of the ordinary?

Maybe someday I'll have a nice quote here.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am not sure what you mean by LTO and -gc-sections.

For reference, I add my current compiler and optimization settings here:

Aha!  Well done...

 -gc-sections you have at least partially selected, here:

(Compile command line)
   -mthumb -D__SAMD21G16B__ -DF_CPU=48000000UL -DBOARD=USER_BOARD -D__SAMD21G16B__ -DARM_MATH_CM0PLUS=true -DSYSTICK_MODE
     -DUSART_CALLBACK_MODE=true -DADC_CALLBACK_MODE=false -DTC_ASYNC=true -DTCC_ASYNC=false -DWDT_CALLBACK_MODE=true
     -DI2S_CALLBACK_MODE=false -DCONFIG_HERO_RELEASE -DI2S_CALLBACK_MODE=true -U__SAMD21J18A__  -Os -ffunction-sections
     -fno-rtti -fno-exceptions -mlong-calls -g3 -Wall -mcpu=cortex-m0plus -c -MD -MP -MF "$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -MT"$(@:%.o=%.o)"

And here:

(Link command line)
-mthumb -Wl,-Map="$(OutputFileName).map" -Wl,--start-group -larm_cortexM0l_math -lm  -Wl,--end-group
-L"../src/ASF/thirdparty/CMSIS/Lib/GCC"  -Wl,--gc-sections -mcpu=cortex-m0plus
 -T../src/ASF/sam0/utils/linker_scripts/samd21/gcc/samd21g16b_flash.ld -Xlinker --defsym=STACK_SIZE=0x400

though you may want to add the "prepare data for garbage collection" as well, in the compiler options.

 

However, you are missing an important option that I didn't think of through the first round.

In linker options, "General", you should check the "use size-optimized library" option.   This should make a BIG difference!

 

 

Also I am not sure how to use “nm -S —size-sort foo.elf” as you told me. I will try to Google this now.

"nm" is a cli tool that displays symbol tables from a .elf file.  You'd want to use it from a "Atmel Studio Command Line" window, after finding the .elf file for your project (probably in projectdir\debug.)   For example (this is an Arduino sketch compiled in AS, so it'll look a bit different from your program.):

 

pwd
C:\Users\billw\Documents\Atmel Studio\7.0\ZeroSketch\ZeroSketch\MyAsciiTable\Debug

C:\Users\billw\Documents\Atmel Studio\7.0\ZeroSketch\ZeroSketch\MyAsciiTable\Debug> arm-none-eabi-nm -SC --size-sort MyAsciiTable.elf
200004e0 00000001 B _dry_run
200005e1 00000001 B _pack_message
20000154 00000001 b rxLEDPulse
20000155 00000001 b txLEDPulse
   :
00001878 00000154 T USBDeviceClass::ISRHandler()
000004b0 00000160 T SystemInit
20000160 000001c0 b udd_ep_in_cache_buffer
20000320 000001c0 b udd_ep_out_cache_buffer
200006f0 0000023c B Serial
2000092c 0000023c B Serial1
000025e8 00000450 T g_APinDescription

 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear westfw,

 

I think the  "use size optimized library" option worked! For the first time my project builds successfully for my custom designed PCB with the ATSAMD21G16! But I cannot see if the code actually works because creating ClassOne dynamically works, but creating ClassTwo dynamically does not. Probably the system does not have enough RAM now...  

 

Also great explanation for the "nm" tool. I would want to compare the two builds (for the bigger ATSAMD21J18 (256 KB) without "use size optimized library", and for the smaller ATSAMD21G16 (64 KB) with "use size optimized library" toggled) to see the actual difference. But I will go on with this project in about two weeks so I cannot improve and debug the memory usage yet.

 

Thanks again westfw!

Maybe someday I'll have a nice quote here.