Let's Make Robots!

Library Basics - Optimization

The first Library Basics blog covered a basic conversion from a working sketch to a very simple library using the C++ class.  User mogul correctly pointed out some code optimizations that we can make.  His original message was:


You seem to waste a little resources here and there:

You store 4 private variables.

  • sensorPin could have been a 8bit type (uint8_t)
  • duration should have been unsigned long. (more "correct", but not an optimization)
  • cm and inches can be omitted

The current code calculates both inches and cm on all pings, but in real life applications often  only the one of them will be needed. I suggest to make the Ping::pulse ping and store in duration, and then let the two accessor functions do the conversions. This way you do not need the functions Ping::microsecondsToInches and Ping::microsecondsToCentimeters

And then it's a bit unconventional to have actual functionality placed in the .h file. Even if its only a single line of code the 3 return statements should not be there. You should have put them and their methods i the .cpp file and left the pure prototypes in the .h fiile.


Let's make these changes to see how they would look.  First let's look at the header file.


Ping.h

#if (ARDUINO >= 100)
#include <Arduino.h>
#else
#include <WProgram.h>
#endif

#ifndef PING_H
#define PING_H

class Ping
{
  private:
    uint8_t sensorPin;              // pin to use for the sensor
    unsigned long duration;       // microseconds of the ultrasonic pulse

    Ping();          // don't allow default constructor

  public:
    Ping(uint8_t pin);      // constructor
    void pulse();             // send the command to send a ultrasonic pulse

    unsigned long getInches();              // get our reading in inches
    unsigned long getCentimeters();      // get our reading in cm
    unsigned long getMicroseconds();   // get our reading in microseconds
};

#endif


Some notes on the header file:

  1. If #if code involving the ARDUINO definition is used to ensure we put the proper Arduino header in for the 1.0 version of the IDE vs the older versions.
  2. I moved all code to the '.cpp' file.  There may be a bit of style or some programming standards that you may want to use, so just Google a few and see what seems to make the most sense to you. I don't really have a true preference but I've seen very simple methods in the header file and people make a good argument to put all code in the '.cpp' file.

Now our changes to the '.cpp' file.


Ping.cpp

#include "Ping.h"

Ping::Ping() {
  // Do nothing
}

Ping::Ping(uint8_t pin) {
  sensorPin = pin;
}

void Ping::pulse() {
  // Tell the sensor to send a signal

  pinMode(sensorPin, OUTPUT);
  digitalWrite(sensorPin, LOW);
  delayMicroseconds(2);
  digitalWrite(sensorPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(sensorPin, LOW);

  // Read the duration of the ping 

  pinMode(sensorPin, INPUT);
  duration = pulseIn(sensorPin, HIGH);
}

unsigned long Ping::getCentimeters() {
  return duration / 58;
}

unsigned long Ping::getInches() {
  return duration / 148;
}

unsigned long Ping::getMicroseconds() {
  return duration;
}


The code file:

  1. I simplified the getInches() method calculations since we could do some of the divisions ahead of time.
  2. Calculations are done when they are need, not every time we measure the ultrasonic pulse.

Conclusion

Original size: 3432 bytes
Library size: 3518 bytes
Optimized size: 3368 bytes

Converting our library from our working, non-class code had an increase of 86 bytes.  By going over our code and eliminating variables that are not needed, doing any precalculations and reducing our variable datatypes to the smallest types that accomodate our values, we saved 64 bytes from our original code.

Maus



Update 2012-2-09

After discussing the formulas with Mogul and others in the shout box, I've updated the code above to use the proper conversion formulas.  These numbers are working with my current robot code and seem to be doing fine.

Maus

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

getMicroseconds() function seems to be gone.

I'm not sure why you have to have the empty constructor along with the real one.

Otherwise it looks good. And interesting that the final code actually got smaller than the original. Unfortunately we don't have an easy way to know how much ram space a given program will consume, which is another important resource to keep an eye on.

I don't know how I missed the getMicroseconds() function, so that is fixed. Thanks for catching that.

As far as the empty Ping::Ping() constructor, I was under the impression that you needed to create the constructor and place it in the private section to not allow an empty constructor for the user.  I could be mistaken or thinking of something else.  I plan on trying to remove that code tonight and see the results.  Hopefully a few more bytes saved.

Speaking of results, I tried simplifying the the getCentimeters() calculation from 'duration / 29 / 2' to 'duration / 14.5' and the size of the code jumped up to about 3950 bytes.  It seems that floating point calculations take up quite a few resources.

Maus

Yes, the float library is heavy, and computations there are time consuming as well.

Are you sure you actually want to divide with 14.5 and not 58....

You are correct.

Going from 'x / 29 / 2' is not 'x / 14.5'  that would not produce the proper calculation where 'x / 58' would.

Original: 58 / 29 = 2 then 2 / 2 = 1, correct

My messup: 58 / 14.5 = 4, incorrect

Corrected: 58 / 58 = 1, correct

Maus

If the simplification of the getCentimeters' calculation from "duration / 29 / 2" to "duration / 14.5" is wrong then the calculation of getInches is wrong too.
58 microseconds = 1 centimeter and (according to "duration / 37") 1.56756757in, but 1cm is only 0.393700787in.

If getInches' calculation is changed from "duration / 37" to "duration / 74 / 2" (as per itead's library) then we have
58 microseconds = 1 centimeter and 0.391891892in, which is only 0.001808895in different.

basile