Forums

C++ and stuff on embedded processors, again

Started by bitrex September 30, 2017
So conventionally the idea is that things like dynamic memory 
allocation, smart pointers, modern C++ abstraction and templates etc. is 
too "heavyweight" to use on 8 bit processors.

Not really true at all, here's the output from a smart pointer 
implementation I wrote using a custom embedded-appropriate memory 
allocation policy in straight C++11 for the AVR Mega series; this is the 
output over the hardware serial port from an ATMega328 to a PC terminal:

<https://imgur.com/a/kYefH>

This is just some structs being allocated dynamically to a memory pool 
on the heap inside a function, managed by the lightweight smart pointer 
object on the stack, which then de-allocates the heap memory 
automatically when the smart pointer object goes out of scope as the 
function returns. The total codebase size is just under 1k SLOC, which 
compiles down to using about 5.5 kb of program memory space on the 
device. SRAM usage is dependent on the size of the pool/type of managed 
object


On Sat, 30 Sep 2017 11:10:41 -0400, bitrex wrote:

[...]

An OT in the subject line would be appreciated in future, thanks very 
much.



-- 
This message may be freely reproduced without limit or charge only via 
the Usenet protocol. Reproduction in whole or part through other 
protocols, whether for profit or not, is conditional upon a charge of 
GBP10.00 per reproduction. Publication in this manner via non-Usenet 
protocols constitutes acceptance of this condition.
On Sat, 30 Sep 2017 11:10:41 -0400, bitrex
<bitrex@de.lete.earthlink.net> wrote:

>So conventionally the idea is that things like dynamic memory >allocation, smart pointers, modern C++ abstraction and templates etc. is >too "heavyweight" to use on 8 bit processors. > >Not really true at all, here's the output from a smart pointer >implementation I wrote using a custom embedded-appropriate memory >allocation policy in straight C++11 for the AVR Mega series; this is the >output over the hardware serial port from an ATMega328 to a PC terminal: > ><https://imgur.com/a/kYefH> > >This is just some structs being allocated dynamically to a memory pool >on the heap inside a function, managed by the lightweight smart pointer >object on the stack, which then de-allocates the heap memory >automatically when the smart pointer object goes out of scope as the >function returns. The total codebase size is just under 1k SLOC, which >compiles down to using about 5.5 kb of program memory space on the >device. SRAM usage is dependent on the size of the pool/type of managed >object >
Won't that eventually involve some garbage collection? -- John Larkin Highland Technology, Inc lunatic fringe electronics
Cursitor Doom wrote on 9/30/2017 3:14 PM:
> On Sat, 30 Sep 2017 11:10:41 -0400, bitrex wrote: > > [...] > > An OT in the subject line would be appreciated in future, thanks very > much.
Why is electronics off topic here? Are you saying he should be posting politics? -- Rick C Viewed the eclipse at Wintercrest Farms, on the centerline of totality since 1998
On 09/30/2017 03:41 PM, John Larkin wrote:
> On Sat, 30 Sep 2017 11:10:41 -0400, bitrex > <bitrex@de.lete.earthlink.net> wrote: > >> So conventionally the idea is that things like dynamic memory >> allocation, smart pointers, modern C++ abstraction and templates etc. is >> too "heavyweight" to use on 8 bit processors. >> >> Not really true at all, here's the output from a smart pointer >> implementation I wrote using a custom embedded-appropriate memory >> allocation policy in straight C++11 for the AVR Mega series; this is the >> output over the hardware serial port from an ATMega328 to a PC terminal: >> >> <https://imgur.com/a/kYefH> >> >> This is just some structs being allocated dynamically to a memory pool >> on the heap inside a function, managed by the lightweight smart pointer >> object on the stack, which then de-allocates the heap memory >> automatically when the smart pointer object goes out of scope as the >> function returns. The total codebase size is just under 1k SLOC, which >> compiles down to using about 5.5 kb of program memory space on the >> device. SRAM usage is dependent on the size of the pool/type of managed >> object >> > > Won't that eventually involve some garbage collection? > >
Or heap fragmentation. I'd never use free/delete on a processor that small. Of course in a toy implementation where all the malloc()s are the same size, it ought to be okay, but then one might as well use a static array and manage it oneself. Cheers Phil Hobbs -- Dr Philip C D Hobbs Principal Consultant ElectroOptical Innovations LLC Optics, Electro-optics, Photonics, Analog Electronics 160 North State Road #203 Briarcliff Manor NY 10510 hobbs at electrooptical dot net http://electrooptical.net
On 09/30/2017 03:41 PM, John Larkin wrote:
> On Sat, 30 Sep 2017 11:10:41 -0400, bitrex > <bitrex@de.lete.earthlink.net> wrote: > >> So conventionally the idea is that things like dynamic memory >> allocation, smart pointers, modern C++ abstraction and templates etc. is >> too "heavyweight" to use on 8 bit processors. >> >> Not really true at all, here's the output from a smart pointer >> implementation I wrote using a custom embedded-appropriate memory >> allocation policy in straight C++11 for the AVR Mega series; this is the >> output over the hardware serial port from an ATMega328 to a PC terminal: >> >> <https://imgur.com/a/kYefH> >> >> This is just some structs being allocated dynamically to a memory pool >> on the heap inside a function, managed by the lightweight smart pointer >> object on the stack, which then de-allocates the heap memory >> automatically when the smart pointer object goes out of scope as the >> function returns. The total codebase size is just under 1k SLOC, which >> compiles down to using about 5.5 kb of program memory space on the >> device. SRAM usage is dependent on the size of the pool/type of managed >> object >> > > Won't that eventually involve some garbage collection? > >
A reference-counting smart pointer is kind of "semi-automated" garbage collection - each instance maintains a reference to a common control block that keeps track of how many active references to the actual heap allocated struct there are, and decrements the count when an particular pointer instance goes out of scope. Each instance also holds a common reference to the allocated object's destructor, so that when the last refcounting pointer goes out of scope and the count hits zero again it "knows" to fire the destructor and free memory automatically without the user needing to call "delete" on the struct explicitly. Because lil processors like the 8 bit AVR and Cortex M0, etc. only have a small amount of heap and no MMU the standard C++ calls "new" and "delete" aren't used in the implementation, but a custom allocator where a big pool of some user-defined size is created at startup, and then blocks are allocated and freed as appropriate. It uses space in the free blocks themselves to store pointers for the logic to determine the memory address of the next free block and the previous used block, like a linked list. So there's naturally the limitation that the user has to define somewhere the maximum number of objects a pool can hold and if you go over that the allocation fails. In this implementation it's not done with old C-style macros but templates, so by simply writing say: struct Foo { //constructor Foo(int a) { _a = a; } //structure fields int _a; }; void somefunc() { auto my_ptr1 = allocated_shared<my_custom_allocator, 5, Foo>(1); auto my_ptr2 = allocated_shared<my_custom_allocator, 5, Foo>(2); } the compiler automatically "knows" to create a custom allocator block in SRAM, make it big enough to hold 5 structures of type "Foo", and call Foo's constructor with the argument in parenthesis returning a smart pointer. "my_ptr1" and "my_ptr2" then behaves exactly like a regular C/C++ pointer that you can do all the usual stuff with, except that you don't have to worry about ever explicitly freeing the heap memory the structure is using.
On 09/30/2017 04:35 PM, Phil Hobbs wrote:

>> Won't that eventually involve some garbage collection? >> >> > Or heap fragmentation.&nbsp; I'd never use free/delete on a processor that > small.&nbsp; Of course in a toy implementation where all the malloc()s are > the same size, it ought to be okay, but then one might as well use a > static array and manage it oneself. > > Cheers > > Phil Hobbs >
I wouldn't use the stdlib delete in unmanaged SRAM on a processor that small either. But there are plenty of use cases where allocating/deallocating many small objects of the same size is just what one wants to do! Yep, you absolutely could manage a static array for them yourself. Also very inflexible and error-prone. And you don't need to, the software can handle it for you.
On Sat, 30 Sep 2017 16:37:41 -0400, bitrex
<bitrex@de.lete.earthlink.net> wrote:

>On 09/30/2017 03:41 PM, John Larkin wrote: >> On Sat, 30 Sep 2017 11:10:41 -0400, bitrex >> <bitrex@de.lete.earthlink.net> wrote: >> >>> So conventionally the idea is that things like dynamic memory >>> allocation, smart pointers, modern C++ abstraction and templates etc. is >>> too "heavyweight" to use on 8 bit processors. >>> >>> Not really true at all, here's the output from a smart pointer >>> implementation I wrote using a custom embedded-appropriate memory >>> allocation policy in straight C++11 for the AVR Mega series; this is the >>> output over the hardware serial port from an ATMega328 to a PC terminal: >>> >>> <https://imgur.com/a/kYefH> >>> >>> This is just some structs being allocated dynamically to a memory pool >>> on the heap inside a function, managed by the lightweight smart pointer >>> object on the stack, which then de-allocates the heap memory >>> automatically when the smart pointer object goes out of scope as the >>> function returns. The total codebase size is just under 1k SLOC, which >>> compiles down to using about 5.5 kb of program memory space on the >>> device. SRAM usage is dependent on the size of the pool/type of managed >>> object >>> >> >> Won't that eventually involve some garbage collection? >> >> > >A reference-counting smart pointer is kind of "semi-automated" garbage >collection - each instance maintains a reference to a common control >block that keeps track of how many active references to the actual heap >allocated struct there are, and decrements the count when an particular >pointer instance goes out of scope. Each instance also holds a common >reference to the allocated object's destructor, so that when the last >refcounting pointer goes out of scope and the count hits zero again it >"knows" to fire the destructor and free memory automatically without the >user needing to call "delete" on the struct explicitly. > >Because lil processors like the 8 bit AVR and Cortex M0, etc. only have >a small amount of heap and no MMU the standard C++ calls "new" and >"delete" aren't used in the implementation, but a custom allocator where >a big pool of some user-defined size is created at startup, and then >blocks are allocated and freed as appropriate. It uses space in the free >blocks themselves to store pointers for the logic to determine the >memory address of the next free block and the previous used block, like >a linked list. > >So there's naturally the limitation that the user has to define >somewhere the maximum number of objects a pool can hold and if you go >over that the allocation fails. In this implementation it's not done >with old C-style macros but templates, so by simply writing say: > >struct Foo { > > //constructor > Foo(int a) > { > _a = a; > } > > //structure fields > int _a; >}; > >void somefunc() >{ > auto my_ptr1 = allocated_shared<my_custom_allocator, 5, Foo>(1); > auto my_ptr2 = allocated_shared<my_custom_allocator, 5, Foo>(2); >} > >the compiler automatically "knows" to create a custom allocator block in >SRAM, make it big enough to hold 5 structures of type "Foo", and call >Foo's constructor with the argument in parenthesis returning a smart >pointer. "my_ptr1" and "my_ptr2" then behaves exactly like a regular >C/C++ pointer that you can do all the usual stuff with, except that you >don't have to worry about ever explicitly freeing the heap memory the >structure is using.
Good grief, why? Looks a lot like "it's more fun to play with fancy tools than code the boring application." I've seen way too much of that. -- John Larkin Highland Technology, Inc lunatic fringe electronics
On 09/30/2017 04:04 PM, rickman wrote:
> Cursitor Doom wrote on 9/30/2017 3:14 PM: >> On Sat, 30 Sep 2017 11:10:41 -0400, bitrex wrote: >> >> [...] >> >> An OT in the subject line would be appreciated in future, thanks very >> much. > > Why is electronics off topic here?&nbsp; Are you saying he should be posting > politics? >
lol
On 09/30/2017 04:47 PM, John Larkin wrote:

>> the compiler automatically "knows" to create a custom allocator block in >> SRAM, make it big enough to hold 5 structures of type "Foo", and call >> Foo's constructor with the argument in parenthesis returning a smart >> pointer. "my_ptr1" and "my_ptr2" then behaves exactly like a regular >> C/C++ pointer that you can do all the usual stuff with, except that you >> don't have to worry about ever explicitly freeing the heap memory the >> structure is using. > > Good grief, why? > > Looks a lot like "it's more fun to play with fancy tools than code the > boring application." I've seen way too much of that.
In PowerBASIC!