搜索
bottom↓
回复: 27

在32位单片机上使用32位格式的数据效率最高?

[复制链接]

出0入0汤圆

发表于 2017-3-22 15:19:32 | 显示全部楼层 |阅读模式
如题:这是我看网上一位大神说的,出处不记得了,想求证下,顺便请大神解释下原因...
我详细说下,他表达的大概意思是这样的:
因为32位单片机(例如stm32)指令是ARM/THumb混编,而使用ARM指令集效率最高,所以,应该尽量使用32bit数据,例如,假设你需要申请一个0~10的变量,正常情况下我们使用u8就可以了,但是为了效率的提高,我们可以使用int/unsigned int来定义这个变量,这是一种典型的空间换时间的思想".
在下看来,如果上面的说法是准确的,很多时候单片机的flash都是会有剩余的,如果这样的确能带来哪怕效率的一点点提升,我觉得都是值得的.
这种说法准确吗?有什么办法可以验证下?
请进来的大神发表下看法,谢谢哈

出0入0汤圆

发表于 2017-3-22 15:36:36 | 显示全部楼层
1. stm32不支持arm32位指令, 只支持thumb-2
2. 的确int比u8效率高, 但高的有限

出0入0汤圆

发表于 2017-3-22 15:39:21 | 显示全部楼层
但具体又要看什么操作,机器指令有何区别,并不一定所有的指令都是32位优于8位,但总地来说,是要尽可能地用32位变量,这样写出来的C代码不挑环境。
变量是放在RAM里面的,关于Flash代码中的常量初始化的效率,楼主可以写个循环试试,在函数里面频繁地使用初始化变量。

出0入93汤圆

发表于 2017-3-22 15:56:58 | 显示全部楼层
本帖最后由 takashiki 于 2017-3-22 15:58 编辑

你把那个括号里面的例如stm32换成ARM7/ARM9,这个结论是成立的。
然而,还要受到很多制约。比如外扩总线,假如外扩的是16位总线,这时32位的ARM指令效率大多反而不如16位的Thumb。
对于STM32来说,根本就没有32位的ARM指令了。32位效率比8位高。但是CM加入了专门的8位、16位访问,效率高得也并不是很多。

然后,您加黑的这句话语文老师看到会气死的,前后无法构成因果关系。指令是指令,数据是数据,不能因为32位的指令就要32位的数据。大多8位RISC指令集指令长度是16、14、13、12位的,但从没见过有12~14位长的数据。

出0入0汤圆

发表于 2017-3-22 16:44:02 来自手机 | 显示全部楼层
一般用32位变量编译出来会少一些指令,因为在cpu寄存器运算是32位,如果要存8位到ram需要额外指令来截断

出0入0汤圆

 楼主| 发表于 2017-3-22 17:25:08 | 显示全部楼层
额,我语文似乎表达的确不是很好
在此感谢大神指教了
我想表达的意思也很清楚
32位的单片机,对指令的寻址和对数据的寻址主要都是32位的,是吧

出0入0汤圆

发表于 2017-3-22 20:37:13 | 显示全部楼层
takashiki 发表于 2017-3-22 15:56
你把那个括号里面的例如stm32换成ARM7/ARM9,这个结论是成立的。
然而,还要受到很多制约。比如外扩总线, ...

语文老师笑尿

出0入0汤圆

发表于 2017-3-22 20:50:47 | 显示全部楼层
有专门的指令,不用分拆数据的话效率一样,否则平台决定的位宽更好

比如cortex-m0里面32bit效率比8-bit效率高

cortex-m3,记得32bit和8bit效率一样的

出0入0汤圆

发表于 2017-3-22 21:20:47 | 显示全部楼层
是不是真的,为何看起都以1字节 做寻址单位,如FSMC接口, 用16位宽时最低地址线是A1 为什么不是A0,感觉内部还是以1字节为单位在寻址,那u8类型 有什么难操作的

出0入475汤圆

发表于 2017-3-23 00:08:38 来自手机 | 显示全部楼层
我也在纠结这个问题,以前8位机特别注意变量能用u8绝不会用u16,更别说int了,因为非常明显的感觉到编译后的代码空间大小实在差异太大,所以一旦有移植过来的代码都手工一个变量一个变量的修改过的。但是现在32位没有太在意这个问题,不过很多地方还会习惯性的知道u8足够的范围就不会用32位变量

出0入8汤圆

发表于 2017-3-23 08:54:16 | 显示全部楼层
我说一个对于 32 位体系通用个概念吧:
一般来说,对于不能直接进行 8 位或者 16 位运算的 CPU 而言,
对于这些位宽的变量的表达式,编译器,就需要花费额外的指令,来确保这些数据类型的运算,符合 8 位或者 16 位的运算规则,例如数据该溢出时,能够溢出之类的。
所以对于位宽不敏感的场景,都尽量用与体系一致的位宽。

出0入0汤圆

 楼主| 发表于 2017-3-23 09:19:09 | 显示全部楼层
多少有点明白了,谢谢楼上这位大神

出0入0汤圆

发表于 2017-3-23 09:23:37 | 显示全部楼层
C语言里,是以INT为基础的。

可以说INT效率最高。不过不必太重视,效率提升有限。

差别在什么地方呢?

差别就在于,以一个变量加一而论。
ARM的寄存器是32位额,所以要取八位,扩展到32位,然后加1,最后把8位数据放回去。

如果是32位,就不需要扩展。

但是最新的ARM指令,二者执行时间是一样的。

出0入0汤圆

发表于 2017-3-23 09:40:47 | 显示全部楼层
CM3下执行时间差异很小,但若是全局变量的话RAM占用就差大了

出0入0汤圆

发表于 2017-3-23 13:21:32 | 显示全部楼层
对于C而言,无符号整数数据类型有回绕的性质(C99 6.2.5-9)。有符号的我懒得查了。这个性质是标准定义的,编译器在具体体系架构上必须实现的。

所以,在某个平台上,计算过程中使用长度和寄存器一致的变量,一定是最高效的。如果使用短类型,就要看指令集是否提供相应的指令了,效率有可能降低。

对ARM平台来说,就是32bit计算好使;16bit的就可能需要编译器插入额外指令处理回绕/溢出,降低效率。
对X86平台,因为指令集提供了各种长度的指令,处理效率基本就没区别,反而有可能小数据类型的指令短,效率更高(CISC么……)

由于处理器的存储带宽是有限且宝贵的,一般来说,应当考虑使用最小的必须类型保存数据,捞进来后用REG宽度计算,完成后裁成小的写回。
当然,这中间的溢出行为要自己考虑清楚。

这个具体可以看stdint里定义的[u]intxx_t和fastxx_t之间的区别。理论上,不依赖于回绕/溢出行为的,用fast_t系列,比如for的index之类。

此外,截止到目前,在考虑兼容16-64位的情况下,int类型在多数时候都能提供最佳性能。128位时代就不一定了。

出0入0汤圆

发表于 2017-3-23 13:53:17 | 显示全部楼层
本帖最后由 creep 于 2017-3-23 13:55 编辑

出0入0汤圆

发表于 2017-3-23 14:05:58 | 显示全部楼层
Mark,学习一下,大部分时间都是能用8位就不用16位,51时代留下的习惯

出0入0汤圆

发表于 2017-3-23 15:30:12 | 显示全部楼层

这个解释是不一样的。

指令相同,而执行时间不一样,必然是指令执行时间不一样。

CM4是3级流水线; CM7是六级流水线。

六级流水线在跳转和存取数据(因为有CACHE)方面必然吃亏。

但是为什么更高档的CM7反而用六级流水线呢?因为流水线长了,频率就可以做上去。所以STM32F7可以做到400M,而STM32F4只能做到不足200M

出0入0汤圆

 楼主| 发表于 2017-7-20 22:03:42 | 显示全部楼层
今天正好有空, 我做了如下测试:

<1> 环境: STM32F103ZEt6 + MDK5.24.

<2> 此处只是验证了在函数内部使用char, short, 和int的区别.

<3> 我想得出的结论是: 如果我们不是特别在意程序的ROM和RAM, 似乎我们应该养成使用int32_t/uint32_t的好习惯...



多使用一个UXTB指令, 将char扩充为uint32_t.


多使用一个SXTH指令, 将short扩充为int32_t.

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2017-7-20 23:05:18 | 显示全部楼层
一般来讲是的

出0入0汤圆

发表于 2017-11-15 12:35:02 | 显示全部楼层
刚从51转到STM32,正在纠结这个问题。学习了。

出0入0汤圆

发表于 2017-11-15 17:05:51 | 显示全部楼层
以前用STM32F4系列做过测试,求最大值,将所有局部变量改为u32消耗的时间最短

出425入0汤圆

发表于 2017-11-15 21:45:30 | 显示全部楼层
个人的理解:cm3内核,16位和32位的效率是一样的。8位效率低。

出0入0汤圆

 楼主| 发表于 2017-11-16 09:27:29 | 显示全部楼层
我自己理解,对于很多常规运算,cortex-m3已经提供了分别处理8位,16位,32位的指令,所以,理论上效率应该是一样的。

比如说,cortex-m3关于加载数据到寄存器提供了LDRB,LDRSB,LDRH,LDRSH,LDR,LDRD,LDRM(不过我有点不清楚,为什么不提供LDRS,加载有符号的32位数呢)。

出0入0汤圆

发表于 2017-11-16 10:06:11 | 显示全部楼层
擦鞋匠 发表于 2017-11-16 09:27
我自己理解,对于很多常规运算,cortex-m3已经提供了分别处理8位,16位,32位的指令,所以,理论上效率应该 ...

“不过我有点不清楚,为什么不提供LDRS,加载有符号的32位数呢“
  
———— 因为计算机以补码形式存储二进制数,把一个短于寄存器位数的数据加载到寄存器时必须考虑高位符号扩展问题,所以加载短数据的指令需要区分有符号还是无符号以便进行符号扩展。把32位数据加载到32位寄存器不存在符号扩展问题,直接拷贝就行了,不需要额外的有符号加载指令。
  

出0入0汤圆

发表于 2017-11-16 10:23:04 | 显示全部楼层
怎么做效率高和指令集架构以及具体的处理器设计有关系,属于优化范畴。

对CortexM而言:
1 对外通信/数据存储,使用能表达所需数据的最小类型或者归并到32bit读写并适当对齐。
CPU IO带宽一般不足,较少的IO数据量有利于提高效率,节约存储器。
归并到32bit是因为读写8/16bit,实际上总线发的是mask过的32bit操作,能合并就能减少读写数量。
对齐避免异常或者发起多次总线操作。
这个具体看AMBA的技术手册即可。

2 计算时,使用和ALU宽度一致的数据类型计算为宜,但不同的指令集/系统不一样。
对X86来说,指令集提供了8/16/32不同类型的计算指令,此时计算过程使用8/16/32是没区别的。
对ARM来说,指令集只提供32bit的计算能力,对于8/16的数据类型,为确保overflow等行为的正确性,编译器需要根据情况插入指令来确保该行为。
此时,ARM使用ALU同宽的类型会避免额外的指令插入。


标准且通用的方法:
对C99而言,使用stdint标准库是最好的方法:
1 数据存储,或者,需要指定长度的类型,或者,对overflow有要求:使用uint8/16/32_t这种定长类型,会有标准的整数操作行为。
2 临时存储,index,对值的范围有要求,没有对overflow的要求,用fast系列的类型。这个是能保证数据至少有那么多bit,但overflow行为不保证。
这个的计算能确保是最快的。
3 外部数据读入后,根据需要转换为对应的定长或者fast类型进行计算即可。对整数类型而言,就是溢出的问题。

出0入8汤圆

发表于 2017-11-16 10:28:59 | 显示全部楼层
dr2001 发表于 2017-11-16 10:23
怎么做效率高和指令集架构以及具体的处理器设计有关系,属于优化范畴。

对CortexM而言:

好棒,给大神顶一下!

出0入0汤圆

发表于 2017-11-16 10:32:27 | 显示全部楼层
之前有听说过,但是考虑到ram的大小,一直没试过,,
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 20:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表