In embedded development, code size and operating efficiency are very important. Code size often corresponds to the FLASH and RAM capacity of the chip, and the program's operating efficiency also requires running on a processor with corresponding capabilities. In most cases, mature developers hope to reduce code size and improve code operating efficiency, but how to do it specifically? This article will take the compiler of IAR Systems, an internationally renowned compiler manufacturer, as an example to answer the problems that developers often encounter in actual work. Engineer friends can conduct practical verification on the IAR compiler.
For embedded systems, the size and efficiency of the final code depends on the executable code generated by the compiler, not the source code written by the developer; however, source code optimization can help the compiler generate better executable code. Therefore, developers should not only consider the source code system from the perspective of overall efficiency, but also pay close attention to the performance of the compiler and the convenience of compilation optimization.
Compilers with optimizing capabilities can generate executable code that is both small and fast. Compilers achieve optimization by repeatedly transforming the source code. Usually, compiler optimizations follow a well-established mathematical or logical theoretical basis. However, some compiler optimizations are based on heuristic methods. Experience has shown that some code transformations tend to produce better code or open up space for further compiler optimizations.
Compiler optimization only relies on the black technology of the compiler in a few cases. Most of the time, the way you write the source code determines whether the program can be optimized by the compiler. In some cases, even a small change to the source code can have a significant impact on the efficiency of the code generated by the compiler.
This article will talk about things to pay attention to when writing code, but we should first make it clear that we don't need to minimize the amount of code, because even if you use ?:- expressions, post-increment and comma expressions in an expression to eliminate side effects, it will not make the compiler generate more efficient code. This will only make your source code obscure and difficult to maintain. For example, adding a post-increment or assignment in the middle of a complex expression can be easily overlooked when reading the code. Please try to write code in an easy-to-read style.
cycle
Will the following seemingly simple loop report an error?
for (i = 0; i != n; ++i)
{
a[i] = b[i];
}
Although no error will be reported, there are several points that will affect the efficiency of the code generated by the compiler.
For example, the type of an index variable should match that of a pointer.
An array expression like a[i] is actually *(&a[0]+i*sizeof(a[0]), or in layman's terms: add the offset of the i-th element to the pointer to the first element of a. For pointer arithmetic, the type of the index expression should preferably match the type of the pointer to which it points (except for __far pointers, where the type of the pointer to which it points is different from the type of the index expression). If the type of the index expression does not match the type of the pointer to which it points, it must be cast to the correct type before adding it to the pointer.
If in the application, stack space resources (stack is usually placed in RAM) are more valuable than code size resources (code is usually placed in ROM or Flash), you can choose a smaller type for the index variable to reduce the use of stack space, but this often sacrifices code size and execution time (code size becomes larger, execution time becomes slower). Not only that, this conversion will also hinder the optimization of loop code.
In addition to the above issues, we also need to pay attention to the loop condition, because loop optimization can only be performed if the number of iterations can be calculated before entering the loop. However, this calculation is very complicated and is not as simple as subtracting the initial value from the final value and dividing by the increment. For example, if i is an unsigned char, n is an integer, and the value of n is 1000, what will happen? The answer is that the variable i will overflow before it reaches 1000.
While the programmer certainly does not want an infinite loop that repeatedly copies 256 elements from b to a, the compiler cannot know the programmer's intent. It must assume the worst case and cannot apply optimizations that require the number of trips to be provided before entering the loop. In addition, you should also avoid using the relational operators <= and >= in the loop condition if the final value is a variable. If the loop condition is i <= n, then n may be the highest value representable in the type, so the compiler must assume that this is a potentially infinite loop.
Aliases
In general, we do not recommend using global variables. This is because you can modify a global variable from anywhere in the program, and the program will change based on the value of the global variable. This creates complex dependencies that make it hard to understand the program and determine how changing the value of a global variable will affect the program. From the optimizer's perspective, this is even worse because the value of any global variable can be changed by storing a pointer. If a variable can be accessed in multiple ways, this is called aliasing, and aliasing makes the code harder to optimize.
char *buf
void clear_buf()
{
int i;
for (i = 0; i < 128; ++i)
{
buf[i] = 0;
}
}
Although the programmer knows that writing to the buffer pointed to by buf will not change the buf variable itself, the compiler still has to make the worst plan and reload buf from memory in each iteration of the loop.
You can eliminate the aliasing if you pass the address of the buffer as an argument instead of using a global variable:
void clear_buf(char *buf)
{
int i;
for (i = 0; i < 128; ++i)
{
buf[i] = 0;
}
}
With this solution, the pointer buf is not affected by the store through the pointer. As a result, the pointer buf remains unchanged in the loop and its value only needs to be loaded once before the loop, rather than reloaded on each iteration.
However, if you need to pass information between code segments that do not share a caller/callee relationship, then you can simply use global variables. However, for computationally intensive tasks, especially those involving pointer manipulation, it is better to use automatic variables.
Try not to use post-increment and post-decrement
In the following, everything said about postincrement also applies to postdecrement. The standard text on postincrement semantics in C states: "The result of the postfix ++ operator is the value of its operand. After the result is obtained, the value of the operand is incremented". While microcontrollers commonly have addressing modes that increment pointers after load or store operations, few of them can handle other types of postincrement with the same efficiency. To comply with the standard, the compiler must copy the operand to a temporary variable before performing the increment. For straight-line code, the increment can be taken out of the expression and placed after the expression.
For example, the following expression:
foo = a[i++];
Can be changed to
foo = a[i];
i = i + 1;
But what happens if the post-increment is part of the condition in a while loop? Since there is no place to insert the increment after the condition, the increment must be added before the test. For these common but closely related designs for generating executable code efficiency, tools such as IAR Systems' Embedded Workbench provide optimization solutions after summarizing a lot of practices.
For example, the following loop
i = 0;
while (a[i++] != 0)
{
...
}
Should be changed to
loop:
temp = i; /* Save the value of the operand*/
i = temp + 1; /* increment operand */
if (a[temp] == 0) /* use the saved value */
goto no_loop;
...
goto loop;
no_loop:
or
loop:
temp = a[i]; /* use the value of the operand*/
i = i + 1; /* increment operand */
if (temp == 0)
goto no_loop;
...
goto loop;
no_loop:
If the value of i after the loop is not relevant, it is better to put the increment inside the loop. For example, the following almost identical loop
i = 0;
while (a[i] != 0)
{
++i;
...
}
It can be executed without temporary variables:
loop:
if (a[i] == 0)
goto no_loop;
i = i + 1;
...
goto loop;
no_loop:
Optimizing compiler developers are well aware that post-incrementation makes code more complicated to write, and although we have done our best to recognize these patterns and eliminate temporary variables as much as possible, there are always some cases where we cannot produce efficient code, especially when encountering loop conditions that are more complicated than the above. Often, we will split a complex expression into several simpler expressions, just like the loop condition above is split into a test and an increment.
In a C++ environment, the choice of pre-increment or post-increment is even more important. This is because both operator++ and operator-- can be overloaded in both prefix and postfix form. When overloading operators as class objects, it is not necessary to mimic the behavior of primitive operators, but it should be as close as possible. Therefore, classes that can intuitively increment and decrement objects, such as iterators, usually have both prefix (operator++() and operator--()) and postfix forms (operator++(int) and operator--(int)).
Previous article:STMicroelectronics updates TouchGFX software, adds video features to enrich STM32 user experience
Next article:Socionext develops LSI for next-generation cloud labeling to accelerate digital transformation in logistics
Recommended ReadingLatest update time:2024-11-16 13:27
- Popular Resources
- Popular amplifiers
- Huawei's Strategic Department Director Gai Gang: The cumulative installed base of open source Euler operating system exceeds 10 million sets
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- Sn-doped CuO nanostructure-based ethanol gas sensor for real-time drunk driving detection in vehicles
- Design considerations for automotive battery wiring harness
- Do you know all the various motors commonly used in automotive electronics?
- What are the functions of the Internet of Vehicles? What are the uses and benefits of the Internet of Vehicles?
- Power Inverter - A critical safety system for electric vehicles
- Analysis of the information security mechanism of AUTOSAR, the automotive embedded software framework
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- Tuya Smart Module SDK Development Course Series——1. SoC Development Environment Construction
- Designed a Simon game ruler with 4 LEDs and switches
- TMS320C6711 serial communication initialization program
- Data searchRSL10-COIN-GEVB
- MSP430 MCU Development Record (5)
- EEWORLD University Hall----Using JTAG with UCD3138
- Share: What is the use of the 100 Ω resistor before the MOSFET gate?
- POL thermal resistance measurement and SOA evaluation
- Registration for the live broadcast with prizes is in progress: Infineon's intelligent motor drive solution
- [TI Live Review] TI Robot System Learning Kit Lecture (including video, ppt, QA)