Code optimization is usually practiced by using code transformations to enhance performances like: performance time, code size, and lower resource usage. These changes can be made at extreme levels or minimum levels. In programming, optimization is defined as the process of improving a system to increase its efficiency. The system consists of a single PC program, or an assembly of computers or even a whole network system, like the Web. Excellent code optimization makes your code work faster and utilize fewer server resources. You can also make the code easier to examine and manage.
Tips for optimizing C/C++ code
MINIMIZE LOCAL VARIABLES
Local variables are usually kept on the stack, and when you use less of them during optimization, the compiler can store them in registers. In this situation, the function not only has the advantage of accessing memory of data kept in registers but also maintains a strategic distance to the overhead of creating a stack structure. And a point to always remember, never change wholesale to global variables.
DECREASE THE NUMBER OF FUNCTION PARAMETERS
It turns out to be expensive when numerous parameters are used in Function calls. This is for the reason that many parameters push on the stack on every request. For this reason, you are recommended not to pass the entire structures as parameters but introduce pointers and references in this situation.
MAKE DEFAULT CLASS CONSTRUCTORS LIGHTWEIGHT
Beyond what many consider possible, let the constructor be lightweight. Especially for easy, utilized classes such as color, point, or vector: that are frequently modified. The default constructor is always invoked behind your back, and for each object creation. Remember that the compiler will be generating a volatile object above the unique object manifestations in your program. In this way, optimizing the constructor can greatly accelerate the execution. You are also recommended to utilize constructor initializers files for instance: Point::Color() : r(0), g(0), b(0) {} rather than Point::Point() { r= g = b = 0; } .)
DON’T DEFINE A RETURN VALUE IF NOT USED
When the return value is used, the called function will tend to pass it. This is since the called function will not know if it is the return value is being utilized. Therefore, this return value passing can be prevented by not declaring the return value used.
USING OPERATORS
Basic data types
When dealing with basic data types, it is best to use +, -, and *. But the moment +=, -=, *=, and /= operators are used on basic data types; they make the program to be slow. To be specific, you have to be aware of how it gets changed into assembler on your pc.
And for a lot of classes, you are recommended to apply +=, -=, *=, and /= operators, rather than +,-, *, and /. Operators +=, -=, *=, and /= when used here, and they always increase the speed of the program.
For example
Vector u = Vector(1,0,0) + Vector(0,1,0) + Vector(0,0,1); results into five unknown, provisional Vectors: Vector(1,0,0), Vector(0,1,0), Vector(0,0,1), Vector(1,0,0) + Vector(0,1,0), and Vector(1,0,0) + Vector(0,1,0) + Vector(0,0,1).
USE SHIFT OPERATIONS >> AND << INSTEAD OF INTERGER MULITIPLCATION AND DIVISION
Though you are advised to utilize operators >> or << rather than division or multiplication, you should be careful. This is because you will end up creating a terrible mistake. And then to recollect it, you may end up including a few range estimates that will be a bit slower than the initial code you started with.
Bit operators and related stunts can increase the rate of the program. However, you must be extremely careful since you may get a machine-dependent code. And this is something you should stay away from. To be precise, you can still code using add move from the assembler found in C ++. It should be understood that this is an intersection language, and it will bear: assembler coding, problem-oriented arrangements, and object-oriented arrangements. Also, when you include a few libraries, you will end up with a lot of advanced stunts that are generally not applied.
USE Amdahl’s LAW
Amdahl’s law should always be considered when choosing to optimize a particular section of your program. The result on the general program relies upon the time spent in that specific section, which is not often the case from comparing at the code without a performance survey.
Now you recall Amdahl’s law:
- Bearing in mind that funccostrepresents the percentage of your program execution time by the function func, while funcspeedup represents the element by which you accelerate the outlined function.
- Then, when the function TriangleIntersect () is optimized, by the execution time of 40 %, such that it executes twice as quick. Then the program will run 25% quicker as highlighted below:
This implies that the rarely utilized code (for example, The Scene Loader) is likely to be optimized, in any case, at all.
- This is regularly stated as: “Present the basic case quickly and the rare case effectively.”
PREDIX vs. POSTFIX OPERATORS
The moment primitive types are handled, arithmetic operations for the prefix and postfix numbers are likely to be indistinguishable. And when it comes to objects. Postfix operators are expected to make the object create a duplicate of itself to ensure its original state and also trigger the side effects of the action. But the prefix operator does not necessarily require a temporary copy.
CREATE THE RIGHT CODE AND THEN OPIMIZE
The most valuable and challenging thing that devours computer programs is to write the right code. It is extremely easy to write code, but ensuring it is correct is hard. Since every programmer has his/her way of writing the code. The programmer must make sure that he/she writes a right, short, and valid code. If you are sure that the function will be executed often, then proceed to do some optimizations. Finally, create a profile to identify the bottlenecks and eliminate them (by optimizing or enhancing algorithm). Regular improvements in the algorithm will transform the bottleneck, maybe to a function that you probably won’t anticipate. This is a legitimate justification for making apparent optimizations in all the functions you know will often be utilized.
SPEND MORE TIME OPTIMIZING
Highly rated programmers say that you should be more than willing to spend much time optimizing your code as you spend on composing the right code. A lot of people spend a lot of time writing code, but when it comes to optimization, they spend less time. This leads to awful optimizations.
REDUCE USING JUMPS/BRANCHES
- Avoid using too many jumps/branches in the function calls as they are expensive. You are recommended to use two jumps in the function calls and also to stack memory manipulation.
- Always opt to use iteration instead of recursion
- And to avoid function overheads, you are advised to utilize inline functions for brief functions.
- More importantly, learn to place C++ loops inside the functions.
- Chains like long if…else if…else if…often use many jumps near their head. Therefore, you can choose to change to a switch statement. Which most times, the compiler translates into a table query with a single jump. If a switch statement is impossible, place the most widely accepted clauses at the start of the if chain.
CONSIDER THE ORDER OF ARRAY INDICES.
- For C/C++, when array [i][j] and array[i][j+1]are reflected this way, it implies the two arrays are usually stored in one-dimensional memoryand are contiguous. In contrast, when array[i][j] and array[i+1][j] are reflected this way ,it shows the two high-dimensional arrays are separate.
- The speed of the code tends to decrease when you retrieve information stored in physical memory. But when you sequentially retrieve data, the speed of the code is accelerated dramatically.
- If current CPUs access information from primary memory to processor memory, they will retrieve more than one default value. Rather, they load a block of memory consisting of the requested information and the neighboring information. This implies that the array[i][j], after being stored in the CPU memory, array[i][j+1], has a high probability of also being in the memory, while array[i+1][j] will remain in the primary cache.
CONSIDER INSTRUCTION-LEVEL-PARALLELISM.
- Although numerous applications still depend on the execution of individual strings, the current CPUs in a single core have a lot of parallelisms. This implies that a solitary CPU may be running four floating extremity multipliers, running for four memory requirements, and executing a rising branch.
- To use this parallelism, code blocks must have sufficient free addresses to use the CPU fully.
- Always consider making loops to enhance parallelism and also an excellent chance to utilize inline functions.
DECLARING C++ OBJECT VARIABLES
You are recommended to utilize initialization and discard the use of assignment, for instance: (Distance c (straight); is quicker than Distance d; d = straight ;).
BE CAUTIOUS WHEN UTILIZING TABLE-LOOKUP FUNCTIONS
- Numerous individuals utilize tables with pre-calculated values for technical functions (for example, trigonometric functions). And for the case of ray tracing, this becomes essential. Memory queries are extraordinarily (and costly), and it is very likely to restore trigonometric function, for it is worth recovering from memory so quickly.
- In some cases, lookup tables can be precious. In GPU programming, a table lookup is always adapted for technical functions.
DELAY DECLARING LOCAL VARIABLES
- Whenrequired to declare an object variable, a function call is included in the constructor.
- When a variable is needed less often, for example, within an if statement), you are recommended to declare it when mandatory.
BE CAUTIOUS USING TEMPLATES.
- It is different when you optimize different instantiations.
- Although the standard template library is optimized in all aspects, you would dispense with it if you intend to update an interactive ray tracer.
- If you decide to do it by yourself. You will have the advantage of learning the algorithms used, and you will end up knowing the most effective approach to using the code.
- In my experience, the troubleshooting of STL library assemblies is slow. This is generally not an issue, except you are utilizing debug creations for profiling. You will eventually realize that STL builders, iterators, etc. They take up more than 15% of the runtime, which can make it even more confusing to read profile performance.
DISPENSE USING UNNECESSARY DATA INITIALIZATION
If you are trying to initialize a colossal block of memory, you are always recommended to use memset(). It will help you fill the chunk of the memory with a specific value.
CONSIDER TECHNIQUES TO RE-MODIFY YOUR MATH TO ERADICATE EXPENSIVE TASKS.
- Often sqrt () can be omitted, particularly for checks in which a look at the square value produces a similar result.
- If you break x more than once, remember to process and duplicate the effect. It used to be an excellent success for vector normalizations (3 partitions), but lately, It has been noted that this is a problem. But it should be profitable if you make multiple partitions.
- When you apply a C++ loop, ensure calculations that do not shift in-between the iterations are discarded from the loop.
- Always try to find out if you can run values in a loop progressively.
CONCLUSION
Always optimize your program code using the above tips. Since it benefits your code in many ways as highlighted in the above link.