How do you optimize C programs

  • 2020-04-02 01:12:18
  • OfStack

1. Optimization of program structure

1. Writing structure of the program
Although the writing format will not affect the quality of the generated code, but in the actual writing of the program or should follow certain rules of writing, a clear, clear program, for future maintenance. When writing programs, especially While, for, do... While, the if... Elst, switch... Statements such as case or nested combinations of these statements should be written in a "condensed" form,

2. Identifier
Procedures used in the user identifier besides, we must follow the naming rules of identifier, generally don't use algebraic symbols (such as a, b, the x1, y1) as a variable name, should be selected with the meaning of the English word of (or) or hanyu pinyin as identifiers, in order to increase the readability of the program, such as: the count, number1, red, work, etc.

3. Program structure
C language is a high-level programming language, which provides a very complete and standardized flow control structure. Therefore, in the use of C language design MCU application system procedures, the first to pay attention to as far as possible to use structured programming method, so that the entire application system program structure is clear, easy to debug and maintenance. For a larger application, the whole program is usually divided into several modules according to functions, and different modules perform different functions. Each module can be written separately, or even by different programmers. Generally, the functions of a single module are relatively simple, and the design and debugging are relatively easy. In C, a function is considered a module. So-called program modularization, not only to divide the entire program into several functional modules, more importantly, should also pay attention to maintain the relative independence of variables between each module, that is, to maintain the independence of the module, as far as possible to use global variables. For some commonly used functional modules, you can also encapsulate them as an application library that can be called directly when needed. However, when using modularity, if the modules are divided into too small and too fine, the execution efficiency of the program will be reduced (it takes some time to protect and restore registers when entering and exiting a function).

4. Define constants
In the process of programming design, for some constants often used, if it is written directly to the program, once the value of the constant changes, we must find out all the constants in the program one by one, and modify one by one, this will inevitably reduce the maintainability of the program. Therefore, the preprocessing command should be used to define the constants as much as possible, and it can also avoid typing errors.

5. Reduce judgment statements
Where you can use conditional compilation (ifdef), you can use conditional compilation instead of if statements, which helps to reduce the length of the code generated by compilation and reduce the use of judgment sentences without using judgment statements.

6. Expressions
Where the order of precedence of the various operations in an expression is unclear or confusing, you should use parentheses to specify their order of precedence. An expression usually cannot write too complex, if the expression is too complex, after a long time, they are not easy to understand, not conducive to future maintenance.

7, functions,
For the function in the program, before use, the function type should be explained, the function type must ensure that it is the same as the original definition of the function type, for the function with no parameters and no return value type should be added "void". If you need to shorten the length of your code, you can define some common segments of your program as functions, as is the case with high-level optimization in Keil. If you need to shorten the execution time of the program, replace some functions with macro definitions after debugging. Note that macros should be defined after debugging, because most compilation systems do not report errors until after the macros have been expanded, which makes troubleshooting more difficult.

8. Use less global variables and more local variables.
Because global variables are placed in the data store, defining a global variable gives the MCU one less data store space to work with. If too many global variables are defined, the compiler will not have enough memory to allocate. However, local variables are mostly located in the registers inside the MCU. In most MCU, the operation speed of using registers is faster than that of data storage, and the instructions are more flexible, which is conducive to the generation of higher quality code. Moreover, the registers and data storage occupied by local variables can be reused in different modules.

9. Set appropriate compiler options
Many compilers have several different optimization options, so you should understand what each optimization means before you use it, and then choose the most appropriate one. Usually, once the highest optimization is selected, the compiler will pursue code optimization almost pathologically, which may affect the correctness of the program and lead to the program running errors. Therefore, you should be familiar with the compiler you use, and you should know which parameters will be affected and which parameters will not be affected during optimization.
In ICCAVR, there are two optimization options "Default" and "Enable Code Compression".
In CodeVisionAVR, there are two memory modes: "Tiny" and "small."
There are seven different memory mode options in the IAR.
In the GCCAVR, there are more optimization options, and it is easier to select inappropriate options if you are not careful.

Second, code optimization

1. Select the appropriate algorithm and data structure
Should be familiar with the algorithm language, know the advantages and disadvantages of various algorithms, specific information please refer to the corresponding resources, there are many computer books are introduced. The efficiency of program execution can be greatly improved by replacing the slower sequential search method with the faster binary search or out-of-order search method and the insertion sort or bubble sort with quicksort, merge sort or root sort. It is also important to choose the right data structure. For example, if you use a lot of insert and delete instructions in a bunch of randomly stored Numbers, it is much faster to use linked lists.
Arrays and pointer statements have a very password relationship, generally speaking, the pointer is more flexible and concise, and the array is more intuitive, easy to understand. For most compilers, the code generated using Pointers is shorter and more efficient than the code generated using arrays. In Keil, by contrast, the code generated using arrays is shorter than the code generated using Pointers.

Use as small a data type as possible
Variables that can be defined using char, do not use int variables to define; Don't use long int for variables that can be defined using integer variables, and don't use float variables if you can. Of course, do not exceed the scope of the variable after you define it. If you exceed the scope of the variable, the C compiler does not report an error, but the program runs with an error, which is hard to detect.
In ICCAVR, you can set the use of printf parameters in the Options, and try to use basic type parameters (%c, %d, %x, %x, %u, and %s format specifiers), less long integer type parameters (%ld, %lu, %lx, and %lx format specifiers), and try not to use floating point type parameters (%f), as well as other c compilers. All other things being equal, using the %f parameter increases the amount of code generated and slows execution down considerably.

4. Use self-add and self-subtract instructions
Self - add, self - subtract, and compound assignment expressions (a-=1, a+=1, etc.) are commonly used to generate high quality program code. Compilers are usually able to generate instructions such as inc and dec, while many C compilers produce two - to three-byte instructions using instructions such as a=a+1 or a=a-1. In the AVR monolithic application of ICCAVR, GCCAVR, IAR and other C compiler code generated in the above several ways is the same, can also generate high quality inc and dec such as code.

5. Reduce the intensity of calculation
You can replace a complex expression with one that is less computationally intensive but has the same function. As follows:

(1) complementary operation.
        A = a % 8;
Can be changed to:
        A = a, & 7.
Note: bit operations only need one instruction cycle to complete, and most of the C compiler "%" operations are called subroutine to complete, the code is long, slow execution. In general, as long as we find the remainder of 2n squared, we can use the method of bit operation instead.

(2) square operation
        A = pow (a, 2.0);
Can be changed to:
        A = a * a;
Note: in the MCU with built-in hardware multiplier (such as 51 series), the multiplication operation is much faster than the square operation, because the square of floating point is achieved by calling subroutine, in the AVR MCU with built-in hardware multiplier, such as ATMega163, multiplication operation can be completed in only 2 clock cycles. Even in the AVR MCU without built-in hardware multiplier, the subroutine of multiplication operation is shorter than the subroutine code of square operation, and the execution speed is faster.

If I raise it to the third power, for example:
        A = pow (a, 3.0);
Changed to:
        A = a * a * a;
The improvement in efficiency is more obvious.

(3) carry out multiplication and division operation with shift
        A = a * 4;
        B = b / 4.
Can be changed to:
        A = a < < 2;
        B = b > > 2;
Note: usually if you need to multiply or divide by 2n, you can use the shift method instead. In ICCAVR, if you multiply by 2n, you can generate left-shifted code, and multiply by any other integer or divide by any number, you call a multiplication and division method. The shift method is more efficient than the code generated by the multiplication and division method. In fact, as long as it is multiplied or divided by an integer, can use the method of displacement to get the result, such as:
        A = a * 9
Can be changed to:
        A = (a < < 3) + a

6, circulation
(1)
        For tasks that don't involve looping variables, you can put them out of the loop, where the tasks include expressions, function calls, pointer operations, array accesses, etc., you should put all the unnecessary operations together in an init initializer.

(2) delay function:
        The commonly used delay function is in the form of self-addition:
        Void delay (void)
        {
Unsigned int I;
        For (I = 0; i. < 1000; I++)

        }
Change it to self-decreasing delay function:
        Void delay (void)
        {
Unsigned int I;
                For (I = 1000; i. > 0; I -)

        }
The delay effect of the two functions is similar, but almost all C compilations generate 1 to 3 bytes less code for the latter function than for the former one, because almost all MCUS have zero transfer instructions, which can be generated in the latter way.
The same is true with a while loop, where a self-subtracting control loop generates 1 to 3 fewer letters of code than a self-adding control loop.
However, when there are instructions in the loop to read and write the array through the loop variable "I", the use of presubtract loop may make the array overbounds, to be noted.

(3)while loop and do... The while loop
There are two ways to loop while:
Unsigned int I;
        I = 0;
        While (I < 1000).
        {
                I++;
      // user program
        }
Or:
Unsigned int I;
        I = 1000;
        The do
        I -;
        // user program
        While (I > 0);
In both cases, use do... The while loop compiles less code than the while loop.

7, look-up table
Generally do not carry out very complex operations in the program, such as the multiplication and division of floating point Numbers and the square root, as well as some complex mathematical model interpolation operations, for these operations that consume time and consume resources, should try to use the way of looking up the table, and put the data table in the program storage area. If it is difficult to directly generate the required table, also try to calculate first at the start, and then in the data storage to generate the required table, after the program is run directly to look up the table, to reduce the program in the execution of the process of double calculation workload.

8, other
For example, using online assembly and storing strings and constants in program memory are beneficial to optimization.


Related articles: