搜索
bottom↓
回复: 28

各位路过帮忙优化一段C语言16字节左移一位的C代码

[复制链接]

出0入0汤圆

发表于 2019-4-19 10:57:05 | 显示全部楼层 |阅读模式
项目遇到一段调用非常多的代码,运算能力不足,各位路过帮帮看看怎么优化一下.
一段16字节整体左移一位,最后或上一位可变量C代码.跑在stm32或者arm9上.
改查表,汇编都行,谢过.代码如下

  1. uint32_t* st;
  2. st[3] = (st[3] << 1) | ((st[2] >> 31) & 1);
  3. st[2] = (st[2] << 1) | ((st[1] >> 31) & 1);
  4. st[1] = (st[1] << 1) | ((st[0] >> 31) & 1);
  5. st[0] = (st[0] << 1) | v;
复制代码

出0入0汤圆

发表于 2019-4-19 11:10:12 来自手机 | 显示全部楼层
这代码用不了几个时钟周期吧,还是从别的地方着手吧。

出0入362汤圆

发表于 2019-4-19 11:14:06 | 显示全部楼层
<<1没啥好办法吧, >>31再&1这个,应该可以改用位段操作? 不过stm32f0xx不能用

出0入0汤圆

 楼主| 发表于 2019-4-19 11:19:52 | 显示全部楼层

ARM9上IAR开最High speed优化的样子,还是太多指令
能不能汇编批量操作一波

本帖子中包含更多资源

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

x

出0入0汤圆

发表于 2019-4-19 11:24:24 | 显示全部楼层
本帖最后由 NJ8888 于 2019-4-19 11:25 编辑

错了,删了

出0入0汤圆

发表于 2019-4-19 11:26:58 | 显示全部楼层

注意审题要仔细啊

出0入0汤圆

发表于 2019-4-19 12:25:10 来自手机 | 显示全部楼层
zzm24 发表于 2019-4-19 11:19
ARM9上IAR开最High speed优化的样子,还是太多指令
能不能汇编批量操作一波

生成的这个汇编指令基本是最优的了

出0入442汤圆

发表于 2019-4-19 12:29:17 来自手机 | 显示全部楼层
没戏,arm竟没有rol,所以无解,编译器给出的效率是最高的。你要是右移,也得12个周期(ror a0,ror a1,ror a2, ror a3,4个ld,4个st),没省几个周期

出0入0汤圆

发表于 2019-4-19 12:51:22 来自手机 | 显示全部楼层
16x8字节环形缓存,一个字节表示一个位,取终值时组装

出0入0汤圆

发表于 2019-4-19 13:08:08 | 显示全部楼层
说明处理器主频不够用了,超下频看!

出0入8汤圆

发表于 2019-4-19 13:18:02 来自手机 | 显示全部楼层
直接用除法是不是一样?

出0入0汤圆

发表于 2019-4-19 13:27:18 | 显示全部楼层
说下整体的用法和代码量,  最后的性能就差在这么一小段上?

出0入12汤圆

发表于 2019-4-19 13:34:20 | 显示全部楼层
为什么要做这样的操作?CPU 支持 ARM 的 DSP 指令吗?

出0入0汤圆

 楼主| 发表于 2019-4-19 15:10:11 | 显示全部楼层
放弃了,计算错误,以为性能差一点能达到的,原来性能还差2.5倍,ARM9架构要1.7G才达到....
实测用联合体会快一点点


  1. typedef union {
  2.     uint64_t u64;
  3.     uint32_t u32[2];
  4.     uint16_t u16[4];
  5.     uint8_t  uint8_t [8];
  6.     double   f64;
  7.     float    f32[2];
  8. } av_alias64;

  9. #define AV_RNA(s, p)    (((const av_alias##s*)(p))->u##s)
  10. #define AV_RN64A(p) AV_RNA(64, p)

  11. #define AV_WNA(s, p, v) (((av_alias##s*)(p))->u##s = (v))
  12. #define AV_WN64A(p, v) AV_WNA(64, p, v)

  13. uint8_t *status = (uint8_t *)st;
  14. //一楼代码改为下面两行
  15. AV_WN64A(status + 8, (AV_RN64A(status + 8) << 1) | ((AV_RN64A(status) >> 63) & 1));
  16. AV_WN64A(status, (AV_RN64A(status) << 1) | v);
复制代码

出0入0汤圆

发表于 2019-4-19 15:21:48 | 显示全部楼层
wye11083 发表于 2019-4-19 12:29
没戏,arm竟没有rol,所以无解,编译器给出的效率是最高的。你要是右移,也得12个周期(ror a0,ror a1,ro ...

循环左移可以用循环右移搞定的

出0入0汤圆

发表于 2019-4-19 15:25:36 | 显示全部楼层
这不是个移位寄存器么,用个cpld搞定?

出0入442汤圆

发表于 2019-4-19 15:42:30 | 显示全部楼层
myxiaonia 发表于 2019-4-19 15:21
循环左移可以用循环右移搞定的

还真是,中午精力不太好

不过再怎么优化也得12个周期保底,因为risc没有内存操作指令,只能先ld再st。

出0入0汤圆

发表于 2019-4-19 15:57:46 | 显示全部楼层
如果是右移可以加速,用寄存器缓存移位数据,连续16个RRX指令可以完成移位.
  1. RRX 可提供经右移一位后的寄存器中的值。 原先的进位标记将会移入位 [31]。 如果有 S 后缀,则将原先的位 [0] 存入进位标记中。
复制代码

出870入263汤圆

发表于 2019-4-19 17:17:35 | 显示全部楼层
本帖最后由 armstrong 于 2019-4-19 17:20 编辑
zzm24 发表于 2019-4-19 11:19
ARM9上IAR开最High speed优化的样子,还是太多指令
能不能汇编批量操作一波


可以看出,总共4个32位数据,结果却LDR了7次!要知道内存操作都是要2个指令周期的;如果不是片内SRAM,还需要更长时间。
由于是指针操作内存,编译器未能优化,LDR操作过于频繁降低了性能。你可以把4个32位数据一次加载到寄存器,再进行左移操作,最后再写一次回到数组。

出870入263汤圆

发表于 2019-4-19 17:24:36 | 显示全部楼层
本帖最后由 armstrong 于 2019-4-19 17:30 编辑

比如这样,虽然看起来C语句更多,但其实性能更好。
  1. uint32_t* st;
  2. uint32_t a3, a2, a1, a0;
  3. a3 = st[3];
  4. a2 = st[2];
  5. a1 = st[1];
  6. a0 = st[0];
  7. a3 = (a3 << 1) | ((a2 >> 31) & 1);
  8. a2 = (a2 << 1) | ((a1 >> 31) & 1);
  9. a1 = (a1 << 1) | ((a0 >> 31) & 1);
  10. a0 = (a0 << 1) | v;
  11. st[3] = a3;
  12. st[2] = a2;
  13. st[1] = a1;
  14. st[0] = a0;
复制代码

如果能用汇编的LDM和STM就更好了!

补充,如果是在具备cache的CPU上,就把内存的读取和写入改为地址增序排列能发挥cache行的性能。无cache系统则无所谓。

出0入96汤圆

发表于 2019-4-19 17:29:17 | 显示全部楼层
查表会不会快

出0入296汤圆

发表于 2019-4-19 17:42:10 来自手机 | 显示全部楼层
楼主为啥不用 uint64_t ?感觉编译器获得的信息会多一点。至少stm32可能会产生比较优化的代码吧?

出30入54汤圆

发表于 2019-4-19 17:47:10 | 显示全部楼层
这种代码交给编译器,开优化,编译器会帮你优化的。这种移位操作什么的编译器一般都会考虑

出0入296汤圆

发表于 2019-4-19 17:48:31 来自手机 | 显示全部楼层
运算过程中用数组直接操作也不太好,编译器会尽可能遵循原意,这就导致生成了太多没必要的load store指令。可以尝试手工把16个字节按照两个uint64_t的方式读取到两个局部变量里,然后后续操作结束后再把局部变量的值存回数组。这样,至少中间的操作有可能会更多的使用通用寄存器。

出0入0汤圆

发表于 2019-4-19 20:06:24 | 显示全部楼层
  1. __asm void *test(void *st, uint32_t v)
  2. {
  3.         push {r4-r7}
  4.         LDM r0,{r4-r7}
  5.        
  6.         LSL          r7,r7,#1
  7.         LSLS         r6,r6,#1
  8.         ORRCS r7,#1
  9.         LSLS         r5,r5,#1
  10.         ORRCS r6,#1
  11.         LSLS         r4,r4,#1
  12.         ORRCS r5,#1
  13.         RORS         r1,r1,#1
  14.         ORRCS r4,#1
  15.        
  16.         STM        r1,{r4-r7}
  17.         pop {r4-r7}
  18. }
复制代码

出0入0汤圆

发表于 2019-4-19 20:07:00 | 显示全部楼层
  1. __asm void *test(void *st, uint32_t v)
  2. {
  3.         push {r4-r7}
  4.         LDM r0,{r4-r7}
  5.        
  6.         LSL          r7,r7,#1
  7.         LSLS         r6,r6,#1
  8.         ORRCS r7,#1
  9.         LSLS         r5,r5,#1
  10.         ORRCS r6,#1
  11.         LSLS         r4,r4,#1
  12.         ORRCS r5,#1
  13.         RORS         r1,r1,#1
  14.         ORRCS r4,#1
  15.        
  16.         STM        r0,{r4-r7}
  17.         pop {r4-r7}
  18. }
复制代码

出0入0汤圆

发表于 2019-4-20 09:40:10 | 显示全部楼层
呼一下拖拉机大神dr2001吧

出0入0汤圆

 楼主| 发表于 2019-4-20 10:40:19 | 显示全部楼层
楼上代码都试了.效率最快的是转uint64*交给编译器优化

  1. uint64_t* st;
  2. st[1] = (st[1] << 1) | ((st[0] >> 63) & 1);
  3. st[0] = (st[0] << 1) | v;
复制代码

出0入296汤圆

发表于 2019-4-20 18:19:54 | 显示全部楼层
zzm24 发表于 2019-4-20 10:40
楼上代码都试了.效率最快的是转uint64*交给编译器优化

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

本版积分规则

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

GMT+8, 2024-4-19 10:48

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

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