Performance Tuning of Code – 13 Issues Explained

If an application runs and performs all required task for the user or client, doesn’t means that it is complete in all the sense. Using performance analysis tools like profiler we can find out how our application is performing internally means in terms of speed, memory usage, and power utilization.

Tuning The Code
Tuning The Code

There are lots of issues which can reduce the performance of your application; below I am listing some of them

Issue 1: Dll Usage Optimization:

In Symbian we have two types of libraries in terms of there linkage that is static interface DLL and polymorphic dll. Many times applications statically linked with many dll which may or may not be required depending upon the different use cases but all get loaded into memory which can reduce the application performance because the static dependency of the application or server can have large impact on the free RAM. Static dependencies can be minimized by splitting the logical functionalities into dynamically loadable components.

void RuntimeLoadDllL() { RFs file session; User::LeaveIfError(fileSession.Connect()); TFileName dllName; If ( SomeCondition ) { ……. dllName = KDllNameOne; } else { ……… dllName = KDllNameTwo; } LoadDllL(dllName); ………………… file session.Close(); } void LoadDllL(const TFileName& aLibName) { library lib; User::LeaveIfErrorlib.Load(aLibName)); ….. }

ECOM also provides a standard mechanism to load specific implementation at run time.

Issue 2: Check usage of TRAPs:

The usage of TRAPs should be as minimal as possible because TRAPs consume more resources than one might assume because it result in kernel executive calls to TTrap::Trap() and TTrap::UnTrap() in addition a struct is allocated at runtime to hold the current contents of the stack in order to return to that state if unwinding proves necessary in the event of leave. If your class is contains more than 6 TRAPs it means your class is not designed properly and we need to redesign it. If possible move TRAPs to the higher levels in function call categories.

Issue 3: Check Server Usage and Optimization

Immediately connecting to the servers when the application starts up might not be advisable as connection to the transient server because it can cause chain reaction, where multiple server dependent components are loaded into RAM.

If the server is rarely used, this approach can be seen as ineffective resource utilization. On-demand connection to a transient is always advisable. Design should be in such a way that client API implementation should be kept separate from the actual server implementation in this way when the client links to the client API library, it will not load server dependencies automatically which should happen after the server is really started.

While designing server always think about its usage and the various resources it needs at runtime. Suppose server provides three features like Read, Write and Notification and read/rite is not very frequent operation in such scenario its better to breakup into two servers one which will provide heavyweight functionality like read/rite/replace, etc.. This will be loaded/unloaded depending upon client requirements and other into always up light-weighted notified server.

Issue 4: Compressing the executable:

Use the compress target statement in the application’s MMP file to specify that the target executable should be compressed, by doing this the code and the data sections of the executable are compressed with the Huffman+LZ77 algorithm.

It allows the stored executables to use less space on the file system. The executable loader decompressed the file when it is loaded. All executable should not be decompressed, only those who can take a large amount of space are a candidate for that.

Issue 5: File Scanning:

Always specify a drive letter in the file paths, if you know that. if the drive letter is not specified, all available drives are searched in the standard symbian OS order, if your file is present in Z: than you are in great loss because z: is always searched last.

Issue 6: Check Memory Usage Optimizations:

In S60 application the default stack size for thread is only 8kB, so only small object should be created on the stack and removed from the stack as early as possible. We should always pass parameters by reference rather than by value, except for basic types, creation of large objects and the array should be done on the heap and always try to minimize the lifetime of auto variable as much as possible.

Issue 7: Check usage of default parameters

If we are declaring the default parameter for function argument, additional code is generated by the compiler which can be a overhead. If the default value is being used often, then it’s worth overloading the method and not providing the default parameter.

void MyFunction( TInt aCounterVal, TInt aParamLength = 2); Can be written as : void MyFunction( TInt aCounterVa ); void MyFunction( TInt aCounterVal, TInt aParamLength );

Issue 8: Check Usage of Reference and Pointer:

Avoid passing a large object by value always prefer reference or pointer. Unless a NULL value is acceptable, a reference is preferable because it is more preferable and simpler to use. Reference cannot be reassigned, if you need to point first an object and later other prefer pointer. References is always preferable than pointer because its more efficient due to the fact that the compiler has to preserve the value of the null pointer through all conversions that is compiler has to test for the NULL value, thus creating further instructions.

Issue 9: Check for inline keyword and Initialization list

Inline keyword doesn’t force compiler to do so, it’s completely on the compiler how should he behave. Avoid use of inline if your function is heavy that is if it contains more than 3-4 statements. Inline methods are advisable when there is some getter or setter methods are there, if we are using trivial constructor for T classes and when we are using thin templates.

There are some more issues related to inline methods like change in the implementation can break binary compatibility and if you have issues like limited memory resource than speed cost of a method call is preferable to the large body of inline code.

Using initialization list for constructors is more efficient than assignment within the constructor body because class member variables are automatically constructed using there default constructor prior to entry within the class constructor itself. However the constructor lists does not allow us to validation of variables and hence they are not always suitable.

Issue 10: Check disk space issue:

Before writing any code we should always think about disk space. Especially when we are writing something on MMC or memory card. . RFs provide Volume() API to handle this issue.

You can get volume information comprising all the information provided by DriveInfo, which additionally gives the volume name, its ID, its size and the amount of free space.

Use RFs::Volume() to get a TVolumeInfo object

TVolumeInfo volumeInfo; ……………….. TInt err=ifsSession.Volume(volumeInfo, EDriveZ); if (err != KErrNone) { …………………… User::LeaveIfError(fsSession.DriveToChar(EDriveZ,driveLetter); …………………. }

Issue 11: Check usage of Descriptors:

Descriptors are designed to be efficient but it completely depends upon there usage. If possible try to minimize use T class descriptors like TPtrC, TPtr, TBuf or TBufC since these are created on the stack (rarely on heap) and should only be used for small strings basically maximum 16 characters are recommended. Always prefer TPtr or TPtrC over TBuf or TBufC. Try to use RBuf in place of HBufC because it makes the code easier to read & understand and hence to maintain and it help us to it reduces the possibility of errors and finally object code is slightly smaller also.

Avoid creation of multiple local instances of class objects like TFileName, TParse etc.., better to have a member variable.

Avoid usage of _L literal macros because of extra runtime overhead associated with construction of temporary descriptor which is the why they are deprecated in production code.

Issue 12: Check usage of Arrays:

Usage of array is completely depends on the requirements. If array size is known at compile time only then TFixedArray can be used. However most likely that a dymanic array is required ,we can go for CArray or pointer arrays like CArrayPtrFlat, CArrayPtrSeg and RPointerArray. In general we always prefer RArray or RPointerArray over CArray or CArrayPtr because of their greater efficiency, flexibility and ease of use,RArrays are recommended over CArray types.

CArray classes re-use the CBufBase framework to provide two alternative storage structures for the array data with very little CArray specific code. There are two assertions for every array access, one in the CArray code and one in the CBufBase code plus a number of TRAP harnesses are used to catch allocation failure. In addition, the CBufBase code operates in terms of byte buffers, and these appear in the API as descriptors. In the implementation, this requires a TPtr8 to be constructed, returned and inspected for every array access. This means that, although very powerful, the implementation of the CArray classes can add considerable overhead for simple vector-like flat arrays of fixed length objects.

Don’t feel that CArray is so bad because the main benefit from the CArray classes is their support for segmented storage and their ability to handle arrays of variable length objects. However, the latter is a rare requirement. and RArray stores objects in the array as word (4 byte) aligned quantities. This means that some member functions do not work when RArray is instantiated for classes of less than 4 bytes in size, or when the class’s alignment requirement is not 4. Be aware that it is possible to get an unhandled exception on hardware that enforces strict alignment. This will affect methods like RArray constructor, Append(..), Insert(..) and [] operator.

Try to avoid RArray if you want to store “C” classes. You should use RPointerArray. The reason is that RArray does not call copy-constructor of your class, but it will do a bit-wise copy that can be dangerous. Therefore RArray is useful for “T” classes and for “C” classes use RPointerArray. One more point that we can not ignore is RArrays impose a limitation of 640 bytes on the size of their elements. Usually this is not an issue, since most objects are likely to be smaller than this limit, and RPointerArray can be used to point to arbitrarily large objects but normally size of object is less than 640 bytes.

Based on runtime extension requirements we can go for Segmented or Flat type. Iteration in the flat arrays is fast because of its contiguous nature where as segment array expansion is fast because internally its implemented as linked list.

Issue 13: Presence of Dead-code :

Normally applications have many releases and newer release can have lines of code (methods/data memebers) which was part of old releases and not required any more in your latest release(s), normally we call it as dead-code.Always remove unwanted dead code beacuse it can increase our ROM usage.