c Efficient Programming in embedded

Source: Internet
Author: User

This article is "Arm embedded system development: Software design and Optimization," a book part of the study notes, the experiment is completed in the IAR. The target board chip is the M3 core.

First, basic C data type

Local variable type

    1. The internal ARM processor is a 32-bit register and a 32-bit data processing operation. Its structural system is RISC Load/store structure. In other words, the data must be loaded from the inside to the internal register before it is used, and any arithmetic or logical "order" cannot be manipulated directly in memory.

    2. 8-bit or 16-bit data is scaled to 32 bits before loading/storing arm. Unsigned number 0 as an extension bit, the signed number is expanded according to the sign bit. This means that loading int type data does not require extra instruction time for bit expansion. Similarly, 8-bit or 16-bit data must be placed in the register's low 8-bit or low 16-bit. In the case of an int or a smaller type of transfer, it is not necessary to store the extra instruction time.
      Example: Where UXTB is a half word to 32 bits for unsigned extensions.

int checksum(int *data){char i;intsum0;for(i=0;i<64;i++)  {    sum += data[i];  }  returnsum;}

Anti-assembly

As you can see for the char type variable i, the compiler first extends it with the uxtb unsigned extension instruction to the word in the operation. Change it to type int.

Anti-assembly

As you can see, the compiler directly does the I operation and does not have an extension instruction.

    1. The ARMV4 architecture-based processor is capable of loading and storing 8-bit, 1-bit, and 32-bit data efficiently. However, most ARM data processor operations are 32-bit. For this reason, local variables should use the 32-bit data type int or long whenever possible. Even when working with 8-bit or 16-bit data, you should avoid using char and short types as local variables. The only exception is when you need to use char or a short type of data overflow zero attribute, such as 255+1=0, to use the char type.

function parameter type

short add_v1(short a,short b){return1));}
 对于编译器。输入值a,b和返回值都存放在32位的ARM寄存器中。编译器是否应该考虑这些32位数值在short类型的范围(-32768 ~+32768)之间呢?或者编译器是否应该通过对低16位数据进行符号位扩展,填充32位寄存器,强制把数值限制在上述范围之间呢。  

Anti-assembly

As you can see, either the input parameter or the return value, the compiler expands it to a 32-bit operation, and the even operation is performed with a 32-bit addition instruction. The return value is also extended to 32-bit returns.
Note:
In the Thumb-2 instruction set, some operations can be done either by a 16-bit instruction or by a 32-bit instruction. For example, for an operation like R0=r0+1, 16-bit and 32-bit instructions provide a mnemonic "ADD" instruction. Under UAL, the assembler can actively decide which to use, or whether it can be manually specified by 16-bit or 32-bit:

ADDS R0, #1; assembler will use 16-bit instructions to save space
ADDS. N R0, #1; Specifies the use of 16-bit instructions (n=narrow)
ADDS. W R0, #1; Specify the use of 32-bit instructions (w=wide)
The W (Wide) suffix specifies a 32-bit instruction. If the suffix is not given, the assembler will first try to use 16-bit instructions to thin the code, and if not, use the 32-bit instruction. Therefore, use the. N "is actually a superfluous, but the assembler may still allow this syntax.
Again, this is the syntax of the arm company assembler, the other assembler may be slightly different, but if the suffix is not given, the assembler will always try to choose a shorter instruction. In fact, in most cases, the application is written in C, and the C compiler will use the short instructions as much as possible. However, a 32-bit instruction is used when the immediate number exceeds a certain range, or if the 32-bit instruction is better suited to an operation.

Summary
For local variables that are stored in registers, do not use the char and short types, except for the 8-bit or 16-bit arithmetic modulo operations, which require the use of a signed or unsigned int type. The division operation is performed faster with unsigned numbers.
For arrays and global variables that are stored in the main storage, it is possible to use small-size data types when the size is met, which saves space. The ARMV4 architecture can efficiently load and store all data widths.
To display (explict) data type conversions by reading arrays or global variables and assigning them to different types of variables, or by writing local variables to different types of arrays or global variables, this conversion allows the compiler to explicitly and quickly process data type extensions that narrow the data width in memory.
Because the display or implicit data type conversions usually have additional instruction overhead, it should be avoided in expressions.
For function parameters and return values, avoid using char and short types as much as possible. Even if the range of parameters is small, you should use the int type to prevent the compiler from making unnecessary conversions.

Two- cycle structure

Fixed number of cycles

int checksum(int *data){  int i;  intsum0;  for(i=0;i<64;i++)  {    sum += data[i];  }  returnsum;}

Anti-assembly

Here, 3 instructions are used to implement the For loop structure:

    • 1 adds instructions, increase the value of I
    • 1 CMP comparison instructions to detect if I is less than 64.
    • 1 BCC conditional directives, if i<64 continues the loop.
      This kind of loop execution efficiency is not high, on arm, a loop in fact, as long as two instructions are enough, as shown below
    • 1 subtraction instructions, to cycle minus the count, and set the result of the condition flag.
    • 1 conditional branching instructions.
int checksum_v1(int *data){  int i;  intsum0;  for(i=64;i!=0;i--)  {    sum += *(data++);  }  returnsum;}

Anti-assembly

No fixed number of cycles

int checksum_v1(intint n){  intsum0;  for(;n!=0;n--)  {    sum += *(data++);  }  returnsum;}

Anti-assembly

At the entrance of the function, the compiler checks whether n is 0, since the array is usually not empty, so the general carcass check is not necessary. The following example shows that using the Do-while loop renders better performance and code density than a for loop.

int checksum_v3(intint n){  intsum0;  do  {    sum += *(data++);  }while(--n!=0);  returnsum;}

Anti-assembly

In fact, in the IAR, when you turn on the Optimization option and select Low, the assembly code is the same as when the two cases are not fixed to the secondary loop.

Loop unfold
In the loop, each loop requires an additional 2 instructions in the loop body: a subtraction instruction to reduce the loop count value and a conditional branch instruction. These directives are often referred to as cyclic overhead.
You can reduce the looping overhead by expanding the loop body-repeating the loop body multiple times and reducing the number of cycles by the same scale, as shown below.

int checksum_v3 (int*Data, unsigned int n) {intsum = 0; Do{sum += *(Data++);sum += *(Data++);sum += *(Data++);sum += *(Data++); N-=4; } while(--N!=0);return sum;}

Anti-assembly

Summarize

    • Use the loop structure minus the count to zero, so that the compiler does not need to allocate a register to hold the loop termination value, and the instructions compared with 0 can be omitted;
    • Using the unsigned loop count value, the loop continues with the condition bit i!=0 instead of i>0, which guarantees that the loop cost is only two instructions.
    • If you know in advance that the loop body executes at least once, then using the Do-while loop is better than the for loop, so the compiler eliminates the need to detect if the loop count is 0, and the important loop body reduces the loop overhead, but not over-expanding, if the cycle overhead is small for the entire program, The loop expansion instead increases the amount of code and reduces cache performance;

three-pointer aliases
When 2 pointers point to the same address object, these 2 pointers are called aliases (alias) for the object, and if one of the pointers is written, it affects the readout from the other pointer. In a function, the compiler usually does not know which pointer is the alias, which one is not, or which pointer has an alias, which one does not, the write to any one of the pointers will affect the readout from any other pointers, but this will significantly reduce the efficiency of code execution.

void timer_v1 (intint*timer2,int*step){  *timer1*step;  *timer2*step;}

Note that the compiler reproduces step two times. Typically, a compiler option called common subexpression elimination (common subexpresion Elimination) allows the compiler to optimize the *step, as long as it is loaded once and the second time it is used, its value will be reused. However, the compiler cannot use this optimization here. The pointer timer1 and the pointer step may be aliases to each other. In other words, the compiler cannot determine whether writing to the pointer timer1 will affect the readout from the pointer step. In this case, the value of the second *step will be different from the first, and will be the value of *timer1. This allows the compiler to add an additional load command.
In Timer_v1, using a local variable, temp, to hold the value of *step, the compiler will not worry about aliases.

void timer_v1 (intint*timer2,int*step){  int*step;  *timer1 += temp;  *timer2 += temp;}

Anti-assembly

Summarize

    • Instead of relying on the compiler to eliminate common sub-expressions containing memory access, a new local variable should be created to protect the value of the expression so that only the expression is evaluated once;
    • Avoid using the address of the local variable, otherwise the rhetorical question of this variable will be less efficient.

c Efficient Programming in embedded

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.