Pages: [1] 2 3   Go Down
Author Topic: Arduino 1.0 Serial - major RAM hog  (Read 4036 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1601
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here are some results for RAM use by Serial in Arduino 1.0 beta 3.

First, output is now buffered but the buffer size has been reduced to 64 bytes.  If you want the same size Serial receive buffer, 128 bytes, as in 0022, you will also get a 128 byte send buffer.

Second, Serial is always loaded, even if it is not used by a sketch.  This happens because of this line in main.cpp:
Code:
    serialEventRun();

I used the MinSerial library from here http://arduino.cc/forum/index.php/topic,72087.0.html to do some tests.  I commented out the above line in main.cpp to prevent loading of Serial when I used MinSerial.

Even if you don't use Serial, 172 bytes of RAM will be allocated on a 328 and 676 bytes of RAM will be allocated on a Mega.

If you increase the buffer size to 128 to match the 0022 receive buffer size, the result is as follows. 300 bytes of RAM on a 328 and 1188 bytes on a Mega.
Logged

Greenville, IL
Offline Offline
Edison Member
*
Karma: 15
Posts: 1328
Warning Novice on board! 0 to 1 chance of errors!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


  This is probably obvious to you fat16lib but, it looks to me the the allocated ram is consistent with the amount of serial ports.  So for the Mega it is no quite 172 x 4 = 684.

 It would be nice if ram allocation was not automatic but, it is easier for a novice to have it allocated by the program.

Quote
Even if you don't use Serial, 172 bytes of RAM will be allocated on a 328 and 676 bytes of RAM will be allocated on a Mega.
Logged


Valencia, Spain
Offline Offline
Faraday Member
**
Karma: 142
Posts: 5301
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here are some results for RAM use by Serial in Arduino 1.0 beta 3.

First, output is now buffered but the buffer size has been reduced to 64 bytes.  If you want the same size Serial receive buffer, 128 bytes, as in 0022, you will also get a 128 byte send buffer.

Second, Serial is always loaded, even if it is not used by a sketch.  This happens because of this line in main.cpp:
Code:
    serialEventRun();


That's not good, you should pay for things you're not using, especially on a microcontroller with (very) limited resources.

Even if you don't use Serial, 172 bytes of RAM will be allocated on a 328 and 676 bytes of RAM will be allocated on a Mega.

That definitely needs fixing IMHO...
Logged

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 124
Posts: 6637
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
    serialEventRun();
There was a fix for this discussed on the Developer list.  It looks like it was implemented in beta4...
(weak symbols used for serialEventRun(), so that if the serial library is not otherwise used, serialEventRun() stays
undefined (0) and the new code reads:
Code:
if (serialEventRun) serialEventRun();
the sort of statement you probably never want to see in your own programs!)
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1601
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Beta 4 fixes problem that Serial is loaded even if you don't use it.

It still has the problem that buffers for all Serial ports are allocated and you can't independently set their size.

This means that if you need a large receive buffer on one port of a Mega, you must accept seven other large buffers.

For example, to log serial data to an SD card reliably requires about 200 ms of Serial receive buffering so data won't be lost during the maximum SD write latency time.

At 115200 this requires about 2500 bytes of buffer.  This is impossible on the Mega since eight equal size buffers are required for a total of 20,000 bytes.
 
Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 124
Posts: 6637
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

But that's not new.  Isn't there a non-blocking API for SD cards?  I was (vaguely) under the impression that they had their own RAM buffers that you filled up, fired off a "program" command, and could go off and do other stuff...
2500 bytes of buffering is well off the bell curve anyway; you might as well write your own serial code and worry about how to prevent it from interfering with the core serial code...
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1601
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is no non-blocking API for SD cards.  The SD spec allows a SD card to go busy for up to 250 ms after you send the data.  You can't determine if the write was successful until after the busy period.  You must wait if busy happens when you are writing a file structure because you have not written data from  the callers buffer.

Even if you could do something else you need the buffering for incoming serial data.

I know that all serial buffers are the same size for a Mega in 0022.  0022 was bad but 1.0 is worse, there are twice as many buffers.

The SD is only one example.  Large serial buffers are useful for other apps.  What is this bell curve that defines proper use of serial?  When is a serial buffer too large?

There is no technical excuse for not having run-time buffer sizes for serial ports.  Commercial embedded kernels have supported run-time serial buffer sizes for thirty years.

Forty years ago I wrote a serial handler for a PDP-8e with run-time buffer sizes.  This machine had 8K words where a word was 12-bits.

« Last Edit: September 13, 2011, 12:27:19 pm by fat16lib » Logged

Forum Administrator
Offline Offline
God Member
*****
Karma: 52
Posts: 639
I find plain exciting
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

@fat16lib

Keep in mind that these betas are designed to figure out this kind of issues. The forum is not the right place to discuss technical implementations. Join the Arduino Developers mailing list and let's discuss it there where we can coordinate what needs to be done to fix it.

One general note is that we have a target for Maker Faire (17/18 September) where the API will be frozen and we send out a Release Candidate of Arduino 1.0, then we'll engage the community in a testing effort to verify if all the features are working correctly.
Some work will need to be done to adapt the libraries to 1.0
About a month later we should have the official release

m
Logged

Valencia, Spain
Offline Offline
Faraday Member
**
Karma: 142
Posts: 5301
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

There is no technical excuse for not having run-time buffer sizes for serial ports.  Commercial embedded kernels have supported run-time serial buffer sizes for thirty years.

When buffers are this large you might as well pay the price for including a copy of malloc() and do it properly.



Logged

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1601
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I agree, you should be able to use malloc().

I have been working on a personal replacement for HardwareSerial.  If you only call begin(), it uses no buffers and no interrupts.  Even sketches with some input work since they tend to read in a tight loop and the serial hardware has a two level input buffer register and an input shift register.

I have two calls to connect interrupts with buffers, one call that uses malloc and sets a status bit to remember to call free and one call to use static arrays in the sketch.

Code:
 bool connectInterrupt(size_t rxSize, size_t txSize); // uses malloc
  bool connectInterrupt(uint8_t* rxBuf, size_t rxSize, uint8_t* txBuf, size_t txSize);
If a buffer is zero length, the corresponding RX or TX will not use interrupts.
« Last Edit: September 13, 2011, 01:57:35 pm by fat16lib » Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 124
Posts: 6637
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

malloc is only about 500 bytes of code on AVR, so I guess it wouldn't be awful.   Of course, the MEGA is already a relatively uncommon arduino.  On the 28pin AVR-based Arduino, the issue is cloudier.  500 bytes of code to be able to modify a 128byte RAM buffer is not a clear win, especially if you can't allocate that 250ms of buffer anyway because that much ram doesn't exist.

What I'd like to see is easier and better documented ways to override core functions in general...
Logged

Valencia, Spain
Offline Offline
Faraday Member
**
Karma: 142
Posts: 5301
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

malloc is only about 500 bytes of code on AVR, so I guess it wouldn't be awful.

Yep. I just did a quick test on my Arduino Uno and sketch size went up by 534 bytes when I used malloc()/free().

To me that's far preferable to using up RAM for no good reason (especially since I know I can use malloc() too...the price has already been paid!)

realloc() used about as much again - not so good...but it's easy to avoid using that.

Logged

No, I don't answer questions sent in private messages (but I do accept thank-you notes...)

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1601
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I agree that it should be easier to override core functions like Serial.

No you can't allocate 2,500 bytes on a 328.  But you can make the input buffer twice as large in 0022 as you will be able to in 1.0.

Here is a user who is logging serial data to an SD at 3088 bytes per second on a 328 by setting the receive buffer size to 600.  This won't be possible with 1.0.

http://arduino.cc/forum/index.php/topic,69263.0.html

Sometimes you want no output buffering and very small input buffers, just enough for debug of a sketch that won't use Serial but uses a lot of RAM.  Other time you want large buffers.  You don't want to edit core files for each app.

In 1.0 you lose more control.

I am close to finishing a reimplementation of HardwareSerial with run-time buffer sizes.  It will be interesting to see how much larger it will be.


Logged

SF Bay Area (USA)
Offline Offline
Tesla Member
***
Karma: 124
Posts: 6637
Strongly opinionated, but not official!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
In 1.0 you lose more control.
I dunno.   in 1.0 you have exactly the same lack of control, but it's "different."

Quote
It will be interesting to see how much larger it will be.
Do the SD libraries already use malloc()?  If so, the hypothetical datalogger should barely notice.
And it's not like anyone has been trying to keep the size of the serial code down, anyway.  It's gotten bigger with pretty much each rev for quite a while now, and as you said elsewhere, the core team resists alternative implementations.  (also wondering if the "print" class has gotten bigger than "printf" yet...)
Logged

0
Offline Offline
Edison Member
*
Karma: 63
Posts: 1601
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Independent of SD libraries, I am interested in a replacement for HardwareSerial with the same API. 

I have done enough code to know that I can produce a smaller library with all the functionality of HardwareSerial in 1.0 beta 4 and independently specify the size of every buffer at compile time.  This is a transparent replacement.

I think I can make a version that is smaller than 1.0 beta 4 with run time sizes if the buffers are user defined static arrays.

I am working on a version that uses malloc/free and it look like it will be 200-500 flash bytes larger than 1.0 beta 4 if you are not using malloc/free for other purposes in your sketch.

I have made this library start in unbuffered mode.  It starts with 0022 style output.  It has just the three characters of hardware input buffering, the two level receive data register and the receive shift register.  Overflow happens when the start bit for the fourth character is detected.

In this mode it is very small and works with most simple sketches that do limited input.

You can call a buffer allocation function to allocate rx and tx buffers and use interrupts.  Calling this function causes malloc/free to be loaded.  It looks like the total flash size will then be 200-500 bytes larger than 1.0 beta 4.

My SdFat library does not use malloc/free. 

I really don't like using the heap in embedded systems except at system start-up.

I worked with critical systems where programming standards forbid using the heap after system start-up.

The Joint Strike Fighter standard is typical http://www2.research.att.com/~bs/JSF-AV-rules.pdf.

Fragmentation of the heap can cause a stack overflow crash with lots of free memory in small chunks in the heap.


Logged

Pages: [1] 2 3   Go Up
Jump to: