[CODE][C++] Factoring to reduce object size

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

In working on a driver for TWI (I2C), I stumbled upon a method that saves 1.2k of memory. This particular snippet had an object size of 1,318 bytes when using doubles in the speed calculation, but coercing them into uint8_t by factoring 1e4, it reduced the object size to 322. Moving that function into definition file, reduced object size by another 92 bytes. As a vehement advocate of assembly, but realizing full well, there are a lot of knowledgeable people, far more than I, that have put together GCC, I'm taking it upon myself to experiment, how some things can be done to optimize space beyond the capability of the compiler.

 

TWI.h    I really like default parameters and it gives 4 possibilities to the same function.

#ifndef TWI_H
#define TWI_H

#define Sys_Clk 1600

#include <avr/io.h>
#include <util/twi.h>

#define SCL PINC5
#define SDA PINC4

class TWI_Interface {
public:
 TWI_Interface (void);
 bool Init (uint8_t Freq = 10, uint8_t Device = 0);
 
private:
 uint8_t Start, End;  // IO buffer indices.
 char  Buffer [64];  // Needs to be this size for wrap-around feature.
 
 uint8_t Speed (uint8_t Freq) { return ((Sys_Clk / Freq) - 16) / 2; }
};

#endif

There are going to be a lot of things I'm going to change as development progresses.  First thing I can see is that defining PORTC does not need to happen for each instance, so I'll be moving it to main.

 

TWI.cpp

#include "TWI.h"

// Default constructor
TWI_Interface::TWI_Interface () {
 Start = End = 0;
 DDRC = (1 << SDA) | (1 << SCL);
}

bool TWI_Interface::Init (uint8_t Freq, uint8_t Device) {
 TWSR &= TW_STATUS_MASK;
 TWCR = (1 << TWEN) | (1 << TWIE) | (1 << TWEA);
 TWBR = Speed(Freq);
 
 if (!Device) {    // Probe for unknown device
  
 } 
 return true;
}

 

main.cpp

#include "TWI.h"

TWI_Interface RTClock;

int main () {
 // Set pins 13 & 12 to ouput on Arduino
 DDRB = (1 << PINB5) | (1 << PINB4);
 
 if ( RTClock.Init() ) {
  do {
  } while (1);
 }
}

 

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

Why does the header have PINC5/4 but main() is setting 5/4 in B?

Why does main() even need to know those pin numbers? Why isn't DDR being set in the class constructor or perhaps passed as parameters to it? (perhaps even default parameters).

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

Why does the header have PINC5/4 but main() is setting 5/4 in B?  

These are indicator LEDs, one being Ardunio onboard and anther tied to pin 12. Just a coincidence SDA & SCL have the same pin numbers as my indicator LEDs on PORTB. 

Why isn't DDR being set in the class constructor or perhaps passed as parameters to it?

 Maybe it wasn't a good idea to include code in it's entirety, as it is work in progress.  The point I was intending to make with this post is instead of using F_CPU = 16000000ul and Freq = 100 kHz requiring doubles, eliminate the unnecessary zeros thereby being able to use uint8_t, yielding the same result and reduce overhead for that particular process by almost 80%.  Lines 6, 10 & 11 of TWI.cpp are going to be moved to main as there is no reason the interface be initialized for each instance.

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

F_CPU is a constant known at compile time so as long as the other factors in the calculation are also known at compile time the entire calculation will be performed at compile time so it makes no difference whether it involves float/double. Or were you thinking that the TWI speed was going to mysteriously going to vary at runtime? 

 

Things like _delay_ms()  and UBRR calculations are all performed on this same principle and cost nothing in terms of run time float code if implemented sensibly because all the inputs to the calculation are constants known at compile time. 

 

Or do you really want to calculate at run time? If F_CPU is something like 11.0592e6 then how do you plan to handle that without avoiding float? 

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

clawson wrote:
Or were you thinking that the TWI speed was going to mysteriously going to vary at runtime?

But it's not inconceivable that it could change.  As an example, I might initialize a channel at 400 khz and my intention is to incorporate a time-out function and if this happens too often, then TWBR would need be calculated for a slower rate; or change pre-scaler.

 

clawson wrote:
If F_CPU is something like 11.0592e6 then how do you plan to handle that without avoiding float?

BCD. Don't know if there is such a thing as BCD library, but it would be pretty easy to implement and it definitely wouldn't take 9k bytes.