运用结构体优化Cortex-M0/M0+程序
2018-02-15 09:38
232 查看
将全局变量组合成结构体,结构体成员变量的数目不超过32个,并按照大小排放,如此可以利用Cortex-M0/M0+的指令集生成最优的代码。
mingdu.zheng at gmail dot com
http://blog.csdn.net/zoomdy/article/details/79304324
转化成汇编
汇编输出如下
两部分的汇编输出对比一下,就很清楚使用结构体的好处了。
使用分散变量,会占用更多的常量池空间,每个全局变量的地址都要存储在常量池,而使用结构体只需要存储结构体首地址,这是因为全局变量存储在什么位置是由链接器决定的,虽然代码中这几个变量是挨着定义的,但是编译器不能假设他们在内存中也是挨着,而结构体作为一个整体,其成员变量一定是挨着的,所以只需要结构体首地址加偏移就可以了。
vs
使用分散变量,会使用更多的指令,每个全局变量都需要单独加载其存储地址,上面的例子中使用了3个全局变量,就有3条LDR指令分别加载他们的地址,而使用了结构体只需要1条LDR指令加载结构体首地址。使用更多的指令意味着需要更多的代码空间以及执行时间。
vs
总结一下,使用结构体组织分散的全局变量既可以节省存储空间,又可以获得更快的执行速度。
为了获得最好的效果,应该尽量生成
结构体的大小应尽量控制在只产生
只有字访问指令,没有半字或字节访问指令,Cortex-M的压栈是以字为单位的,即使程序定义的是半字或字节,压入堆栈的仍然是字。
这两条指令的立即数有8位,可以最多访问256个字。比常规的存储指令访问范围大得多。
mingdu.zheng at gmail dot com
http://blog.csdn.net/zoomdy/article/details/79304324
将全局变量组合成结构体
来看一段很简单的实例代码。// 前半部分使用3个分散的全局变量,将他们初始化成0 int a, b, c; void init1(void) { a = 0; b = 0; c = 0; }
// 后半部分使用结构体将他们组合,同样将他们初始化成0 struct { int a; int b; int c; }t; void init2(void) { t.a = 0; t.b = 0; t.c = 0; }
转化成汇编
arm-none-eabi-gcc -S -O2 -Wall init.c
汇编输出如下
// 前半部分的汇编输出 .global init1 .type init1, %function init1: ldr r0, .L2 // 加载a变量的地址 ldr r1, .L2+4 // 加载b变量的地址 ldr r2, .L2+8 // 加载c变量的地址 mov r3, #0 str r3, [r0] // 设置a变量为0 str r3, [r1] // 设置b变量为0 str r3, [r2] // 设置c变量为0 bx lr .L3: .align 2 .L2: .word a // 常量池存储a变量的地址 .word b // 常量池存储b变量的地址 .word c // 常量池存储c变量的地址
// 后半部分的汇编输出 .global init2 .type init2, %function init2: ldr r3, .L5 // 加载结构体首地址 mov r2, #0 str r2, [r3] // 设置a变量为0 str r2, [r3, #4] // 设置b变量为0 str r2, [r3, #8] // 设置c变量为0 bx lr .L6: .align 2 .L5: .word t // 常量池存储结构体首地址
两部分的汇编输出对比一下,就很清楚使用结构体的好处了。
使用分散变量,会占用更多的常量池空间,每个全局变量的地址都要存储在常量池,而使用结构体只需要存储结构体首地址,这是因为全局变量存储在什么位置是由链接器决定的,虽然代码中这几个变量是挨着定义的,但是编译器不能假设他们在内存中也是挨着,而结构体作为一个整体,其成员变量一定是挨着的,所以只需要结构体首地址加偏移就可以了。
.word a // 常量池存储a变量的地址 .word b // 常量池存储b变量的地址 .word c // 常量池存储c变量的地址
vs
.word t // 常量池存储结构体首地址
使用分散变量,会使用更多的指令,每个全局变量都需要单独加载其存储地址,上面的例子中使用了3个全局变量,就有3条LDR指令分别加载他们的地址,而使用了结构体只需要1条LDR指令加载结构体首地址。使用更多的指令意味着需要更多的代码空间以及执行时间。
ldr r0, .L2 // 加载a变量的地址 ldr r1, .L2+4 // 加载b变量的地址 ldr r2, .L2+8 // 加载c变量的地址
vs
ldr r3, .L5 // 加载结构体首地址
总结一下,使用结构体组织分散的全局变量既可以节省存储空间,又可以获得更快的执行速度。
结构体的大小和排放次序
Cortex-M0/M0+只有16位的LDR/STR指令,其指令形式如下:LDR <Rt>,[<Rn>, <Rm>] // Rt = memory[Rn + Rm] STR <Rt>,[<Rn>, <Rm>] // memory[Rn + Rm] = Rt LDRH <Rt>,[<Rn>, <Rm>] // Rt = memory[Rn + Rm] STRH <Rt>,[<Rn>, <Rm>] // memory[Rn + Rm] = Rt LDRB <Rt>,[<Rn>, <Rm>] // Rt = memory[Rn + Rm] STRB <Rt>,[<Rn>, <Rm>] // memory[Rn + Rm] = Rt LDR <Rt>,[<Rn>, #immed5] // Rt = memory[Rn + ZeroExtend (#immed5<<2)] STR <Rt>,[<Rn>, #immed5] // memory[Rn + ZeroExtend(#immed5<<2)] = Rt LDRH <Rt>,[<Rn>, #immed5] // Rt = memory[Rn + ZeroExtend (#immed5<<1)] STRH <Rt>,[<Rn>, #immed5] // memory[Rn + ZeroExtend(#immed5<<1)] = Rt LDRB <Rt>,[<Rn>, #immed5] // Rt = memory[Rn + ZeroExtend (#immed5)] STRB <Rt>,[<Rn>, #immed5] // memory[Rn + ZeroExtend(#immed5)] = Rt
为了获得最好的效果,应该尽量生成
LDR <Rt>,[<Rn>, #immed5]类型的指令,这种类型的指令直接将偏移地址编码进了指令,不需要额外的指令去处理偏移,倘若编译出
LDR <Rt>,[<Rn>, <Rm>]类型的指令,还需要指令来修改Rm的值来修改偏移。
LDR <Rt>,[<Rn>, #immed5]的指令只有5位的立即数作为偏移值,LDR/STR最多可以偏移31个字(0~124字节偏移范围),LDRH/STRH最多可以偏移31个半字(0~62字节偏移范围),LDRB/STRB最多可以偏移31个字节(0~31字节偏移范围),由此可以得出应该将8位长的变量放在结构体的最前面,16位长的变量次之,32位长的变量放在最后。倘若结构的大小超过32字节,又将8位长的变量放后面,那么生成的就是
LDRB <Rt>,[<Rn>, <Rm>]指令了,那结构体的优势就不明显了。
结构体的大小应尽量控制在只产生
LDR <Rt>,[<Rn>, #immed5]类型的指令,不会产生
LDR <Rt>,[<Rn>, <Rm>]为宜。纯32位成员变量不宜超过128字节,纯16位变量不宜超过64字节,纯8位变量不宜超过31字节。基本上 结构体成员变量的数目不超过32个,并按照大小排放 ,可以做到最好的效果。
临时变量需要使用结构体吗?
不需要,临时变量由编译器决定存储在寄存器或堆栈内。首先编译器会尽量让临时变量存储在寄存器,寄存器不够用的情况下存储在堆栈中,在堆栈中的偏移是编译器控制的,所以编译知道其偏移地址,会自动产生相应的最优指令。Cortex-M0/M0+用于访问堆栈内临时变量的指令如下:LDR <Rt>,[SP, #immed8] // Rt = memory[SP + ZeroExtend(#immed8<<2)] STR <Rt>,[SP, #immed8] // memory[SP + ZeroExtend(#immed8<<2)] = Rt
只有字访问指令,没有半字或字节访问指令,Cortex-M的压栈是以字为单位的,即使程序定义的是半字或字节,压入堆栈的仍然是字。
这两条指令的立即数有8位,可以最多访问256个字。比常规的存储指令访问范围大得多。
相关文章推荐
- 运用DelayLoad来优化运用程序地性能,拦截API(转)
- 改善C++ 程序的150个建议学习之建议10:优化结构体中元素的布局
- 运用Xdebug调试和优化PHP程序
- 一道华为面试题,关于程序优化的
- GCCE编译器优化,缩小目标程序代码
- java 程序性能优化《第二章》设计优化 2.1善用设计模式 1 单例模式
- 运用友元类编写程序,输出时间日期
- 浅谈利用缓存来优化HTML5 Canvas程序的性能
- java性能优化笔记(三)java程序优化
- 新手学java-运用数组写一个五子棋小程序
- 对程序性能优化的总结
- C++程序优化--第一章 简
- 程序优化小记
- iOS程序性能优化
- C程序效率优化(2)
- Java程序性能优化总结--------流程控制篇
- 《深入理解Java虚拟机-->程序编译与代码优化部分
- 程序的优化 文字的减法
- 优化程序性能
- 程序性能和稳定性优化--GDI