|
Optimizing C and C++ Code |
|
|
Minimize local variables If the number of local variables in a function is less, the compiler will be able to fit them into registers. Hence, it will be avoiding frame pointer operations on local variables that are kept on stack. This can result in considerable improvement due to two reasons: - All local variables are in registers so this improves performance over accessing them from memory.
- If no local variables need to be saved on the stack, the compiler will not incur the overhead of setting up and restoring the frame pointer.
Declare local variables in the inner most scope Do not declare all the local variables in the outermost function scope. You will get better performance if local variables are declared in the inner most scope. Consider the example below; here object a is needed only in the error case, so it should be invoked only inside the error check. If this parameter was declared in the outermost scope, all function calls would have incurred the overhead of object a's creation (i.e. invoking the default constructor for a). | Local varialble scope | int foo(char *pName) { if (pName == NULL) { A a; ... return ERROR; } ... return SUCCESS; } | Reduce the number of parameters Function calls with large number of parameters may be expensive due to large number of parameter pushes on stack on each call. For the same reason, avoid passing complete structures as parameters. Use pointers and references in such cases. Use references for parameter passing and return value for types bigger than 4 bytes Passing parameters by value results in the complete parameter being copied on to the stack. This is fine for regular types like integer, pointer etc. These types are generally restricted to four bytes. When passing bigger types, the cost of copying the object on the stack can be prohibitive. In case of classes there will be an additional overhead of invoking the constructor for the temporary copy that is created on the stack. When the function exits the destructor will also be invoked. Thus it is efficient to pass references as parameters. This way you save on the overhead of a temporary object creation, copying and destruction. This optimization can be performed easily without a major impact to the code by replacing pass by value parameters by const references. (It is important to pass const references so that a bug in the called function does not change the actual value of the parameter. Passing bigger objects as return values also has the same performance issues. A temporary return object is created in this case too. Don't define a return value if not used The called function does not "know" if the return value is being used. So, it will always pass the return value. This return value passing may be avoided by not defining a return value which is not being used. Consider locality of reference for code and data The processor keeps data or code that is referenced in cache so that on its next reference if gets it from cache. These cache references are faster. Hence it is recommended that code and data that are being used together should actually be placed together physically. This is actually enforced into the language in C++. In C++, all the object's data is in one place and so is code. When coding is C, the declaration order of related code and functions can be arranged so that closely coupled code and data are declared together. Prefer int over char and short With C and C++ prefer use of int over char and short. The main reason behind this is that C and C++ perform arithmetic operations and parameter passing at integer level, If you have an integer value that can fit in a byte, you should still consider using an int to hold the number. If you use a char, the compiler will first convert the values into integer, perform the operations and then convert back the result to char. Lets consider the following code which presents two functions that perform the same operation with char and int.
|