Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serial class use ram for buffer even if not used #1259

Closed
lestofante opened this issue Feb 1, 2013 · 8 comments
Closed

Serial class use ram for buffer even if not used #1259

lestofante opened this issue Feb 1, 2013 · 8 comments
Milestone

Comments

@lestofante
Copy link

Hi, actually the Serial class always initialize its buffer. This is bad for example on mega, where much ram is "stolen"
I've modded the serial library to allocate the ram only if begin() is called. also the call of end() free the buffer.

This is backward compatible, but has been tested only by me, so it need better testing.
it is based on HardwareSerial.cpp of 1.0.2

code: http://arduino.cc/forum/index.php?action=dlattach;topic=141254.0;attach=33317

@cmaglie
Copy link
Member

cmaglie commented Feb 4, 2013

Interesting, so you just initialized the buffers structure to NULL, and allocate/deallocate the real memory on begin()/end(), right? there are any other changes that I'm ovrelooking?
C

@lestofante
Copy link
Author

yes, other change are just check is buffer is null into ISR, read, write,
available. (in case this method are called when Serial isn't initialized)

If buffer are NULL i return -1 for read, 0 for available and write, and do
nothing for ISR. Old behaviors are respected as the call of end() was
clearing buffer for read and available

I'll try to do a pull request later updated to latest code, but i'm at work
and i don't know if i'll have time or proxy restrictions

2013/2/4 Cristian Maglie notifications@github.com

Interesting, so you just initialized the buffers structure to NULL, and
allocate/deallocate the real memory on begin()/end(), right? there are any
other changes that I'm ovrelooking?
C


Reply to this email directly or view it on GitHubhttps://github.com//issues/1259#issuecomment-13069360.

@damellis
Copy link
Contributor

damellis commented Feb 4, 2013

In the past, other people have expressed concern about using malloc() / dynamic memory for these kinds of things. There's the obvious concern that the allocation could fail, but in general, people seem to have reservations about requiring dynamic memory allocation in core classes. I'm not sure how important those concerns are but they may be worth looking into.

@lestofante
Copy link
Author

i've think about this. There are 3 basic solution:

  1. (actually in use) if malloc fail, begin silently fail and the serial
    won't start, but at least the rest of code should still work.
  2. As above but don't fail silently; begin from void should return an
    boolean, true = Serial is working, false= some error
  3. As above but instead of using only a fixed size buffer, try to use
    smaller buffer until 1 byte buffer, then fail. begin from void should
    return an int, where < 0 = error number, 0=generic error (or serial already
    running?), > 0 = buffer size.

It will be a user's problem check if begin fail, but that way we can be
sure we are back-compatible, as if Serial.begin fail for malloc(), then
this mean the sketch was using too much run and would fail anyway somewhere.

The only real problem i can see is the physical position of the buffer, if
they are created after many local variable, they will live halfway in ram,
and then if the stack grow there can be a stack overflow while there is a
lot of free ram because local variable are dead.
But this is not a real problem: people tends to call begin as first
instruction in the setup, avoiding this pit-fail
People who doesn't do this are people who create and then destroy Serial in
the loop in the hope to save some ram for RAM-hungry elaboration, and then
open thread on forum asking why they doesn't save ram, and then people like
me look what is going on under the scene and provide solution. (see
http://arduino.cc/forum/index.php?topic=141254.0, but it is in Italian)

2013/2/4 David A. Mellis notifications@github.com

In the past, other people have expressed concern about using malloc() /
dynamic memory for these kinds of things. There's the obvious concern that
the allocation could fail, but in general, people seem to have reservations
about requiring dynamic memory allocation in core classes. I'm not sure how
important those concerns are but they may be worth looking into.


Reply to this email directly or view it on GitHubhttps://github.com//issues/1259#issuecomment-13077529.

@PaulStoffregen
Copy link
Sponsor Contributor

On AVR, the compiler and linker somehow know to avoid linking the serial interrupts, if none of the other serial code is actually used. If you never call Serial.begin() or any other serial functions on Arduino Uno, the serial port code and interrupt are not included in the output, and the buffers are not allocated in RAM.

On ARM, I've been searching (so far without success) for a way to duplicate this functionality. How the AVR interrupt vector table is built is something of a mystery to me... at least so far. On ARM, there's an array of addresses that have weak aliases to an unused handler. As soon as any interrupt routine is seen by the linker, that weak alias is replaced by the address of the interrupt routine. The linker must then include the interrupt code, and of course the buffers it uses in RAM, even though nothing else from that file is linked. AVR somehow avoids this. If anyone knows how, I would really be curious to hear?

@lestofante
Copy link
Author

I don't know about interrupt, but I'm pretty sure about ram usage. The
allocation is done at the class initialization, and the class method ate
always used in the hidden main, so the class is always included in the hex
Il giorno 04/feb/2013 19:42, "Paul Stoffregen" notifications@github.com
ha scritto:

On AVR, the compiler and linker somehow know to avoid linking the serial
interrupts, if none of the other serial code is actually used. If you never
call Serial.begin() or any other serial functions on Arduino Uno, the
serial port code and interrupt are not included in the output, and the
buffers are not allocated in RAM.

On ARM, I've been searching (so far without success) for a way to
duplicate this functionality. How the AVR interrupt vector table is built
is something of a mystery to me... at least so far. On ARM, there's an
array of addresses that have weak aliases to an unused handler. As soon as
any interrupt routine is seen by the linker, that weak alias is replaced by
the address of the interrupt routine. The linker must then include the
interrupt code, and of course the buffers it uses in RAM, even though
nothing else from that file is linked. AVR somehow avoids this. If anyone
knows how, I would really be curious to hear?


Reply to this email directly or view it on GitHubhttps://github.com//issues/1259#issuecomment-13092064.

@matthijskooijman
Copy link
Collaborator

@PaulStoffregen, have a look at the comments in #1711 and this post on the mailing list: https://groups.google.com/a/arduino.cc/d/msg/developers/NqRMcgZEKCU/3A2sjSF5h9QJ

Together, they should explain how this stuff works on AVR and perhaps allow you to find out what's different on ARM?

matthijskooijman added a commit to matthijskooijman/Arduino that referenced this issue Dec 18, 2013
By putting the ISRs and HardwareSerial instance for each instance in a
separate compilation unit, the compile will only consider them for
linking when the instance is actually used. The ISR is always referenced
by the compiler runtime and the Serialx_available() function is always
referenced by SerialEventRun(), but both references are weak and thus do
not cause the compilation to be included in the link by themselves.

The effect of this is that when multiple HardwareSerial ports are
available, but not all are used, buffers are only allocated and ISRs are
only included for the serial ports that are used. On the mega, this
lowers memory usage from 653 bytes to just 182 when only using the first
serial port.

On boards with just a single port, there is no change, since the code
and memory was already left out when no serial port was used at all.

This fixes arduino#1425 and fixes arduino#1259.
cmaglie pushed a commit to cmaglie/Arduino that referenced this issue Jan 22, 2014
By putting the ISRs and HardwareSerial instance for each instance in a
separate compilation unit, the compile will only consider them for
linking when the instance is actually used. The ISR is always referenced
by the compiler runtime and the Serialx_available() function is always
referenced by SerialEventRun(), but both references are weak and thus do
not cause the compilation to be included in the link by themselves.

The effect of this is that when multiple HardwareSerial ports are
available, but not all are used, buffers are only allocated and ISRs are
only included for the serial ports that are used. On the mega, this
lowers memory usage from 653 bytes to just 182 when only using the first
serial port.

On boards with just a single port, there is no change, since the code
and memory was already left out when no serial port was used at all.

This fixes arduino#1425 and fixes arduino#1259.
@cmaglie
Copy link
Member

cmaglie commented Jan 28, 2014

Fixed in 1.5.6.

@cmaglie cmaglie closed this as completed Jan 28, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants