Attention paid to making code efficient in speed and particularly in resource use is always worthwhile. This section suggests some methods that should become familiar to developers for this platform.
Each thread in an application has a limited standard stack space of 8Kb, which should be carefully managed. Therefore:
avoid copy-by-value, except for basic types
create any large object or array on the heap rather than the stack
minimise the lifetime of automatic variables by appropriately scoping them
The last point can be illustrated with the following example:
void ABadFunction()
{
TBigObject Object1;
TBigObject Object2;
TBigObject Object3;
GetTwoObjectValues(Object1,Object2);
Object3=SumObjects(Object1,Object2);
FunctionWithUnknownStackOverhead(Object3);
}
In the above code, Object1
and Object2
persist, using stack space, throughout the lifetime of the call to
FunctionWithUnknownStackOverhead()
, although they are not required
by that time. They should be removed from the stack before the call is made.
This can be achieved as follows:
void ABetterFunction()
{
TBigObject Object1;
GetTotalObjectValues(Object1);
FunctionWithUnknownStackOverhead(Object1);
}
void GetTotalObjectValues(TBigObject &aObject)
{
TBigObject Object1;
TBigObject Object2;
GetTwoObjectValues(Object1,Object2);
aObject=SumObjects(Object1,Object2);
}
By splitting the code into two functions, you ensure that the stack is used no more than required.
If a function definition has default arguments, and if that function often gets called with the caller assuming the default arguments, consider providing an overloaded function that doesn't have the additional arguments. This is because every time the compiler supplies a default parameter, it generates additional code where the function is called.
For example, if you have
void FunctionOne(TInt aInt=0);
which often gets called in code by the line
FunctionOne();
then consider supplying
void FunctionOne();
the contents of which might be:
void FunctionOne()
{
FunctionOne(0);
}
Using a reference as a function argument may be more efficient than using a pointer. This is because the compiler has to preserve the value of the null pointer through all conversions.
Imagine a class CXxx
which derives from a mixin class
MYyy
, as in
class CXxx : public CBase,public MYyy {...};
Then, to pass a pointer to a CXxx
to a function taking a
MYyy
, the compiler has to add sizeof(CBase)
to the
pointer, except when that pointer is NULL
. If cp
is a
CXxx*
, and Func()
a function taking an
MYyy*
, then what happens in a call like Func(cp)
is
something like this:
Func((MYyy* aM)(cp==NULL ? NULL : (TUint8*)cp+sizeof(CBase)));
Null references are not possible, so no test for NULL
is
necessary when they are used. On ARM, converting from CXxx*
to
MYyy*
takes 8 instructions, whereas the CXxx&
to
MYyy&
conversion takes only two.
Floating point maths is sufficiently slow that it is worth looking to see if an alternative algorithm using only integer maths is available.
For example, given two TInts
, aTop
, and
aBottom
, instead of:
TReal a = (TReal)aTop;
TReal b = (TReal)aBottom;
TReal c = a/b+0.5;
TReal result;
Math::Round(result,c,0);
return (TInt)result;
you should use
return((2*aTop+aBottom)/(2*aBottom));
Inline functions are intended to speed up code by avoiding the expense of a function call, but retain its modularity by disguising operations as functions. Before using them, however, there are two issues that you should check:
code compactness: limited memory resources may mean that the speed cost of a function call is preferable to large bodies of inline code
binary compatibility: changing the implementation of an inline function can break binary compatibility. This is important if your code is going to be used by other developers.
The most common cases where inline functions are acceptable are:
getter and setters for one- or two-machine word quantities: for example,
inline ConEnv() const { return iConEnv; };
trivial constructors for T
classes:
inline TPoint::TPoint(TInt aX, TInt aY) { iX=aX; iY=aY; };
in the thin-template idiom: see Thin templates
certain other operators and functions, possibly templated, whose definition, not subject to change, is to map one operation onto another, for example,
template <class T> inline T Min(T aLeft,T aRight)
{ return(aLeft<aRight ? aLeft : aRight); }
C++ specifies that delete 0
does nothing, so that you need
never write code such as
if (iX)
delete iX;