Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[kernel]增加一种新的查找字节最低非0位的算法 #5063

Merged
merged 6 commits into from
Nov 11, 2021
Merged

[kernel]增加一种新的查找字节最低非0位的算法 #5063

merged 6 commits into from
Nov 11, 2021

Conversation

Eureka1024
Copy link
Contributor

拉取/合并请求描述:(PR description)

[
这份PR的目的是增加一种新的查找字节最低非0位的算法,该算法比一般的位图算法更省空间,同时对于 32bit以及更长的字节,该算法查找字节最低非0位的实现路径也更加高效。

该算法的原理为:假设 X 为 uint32_t 类型的变量, 则 X & (X - 1) ^ X 的算式可以得到仅含字节最低非0位的结果,也就是将所有的可能转变为仅有的32种可能,也就是32个 “1在不同bit位” 的数,接着将这32个数对 37 取余,能够得到互不相同的结果,利用这些结果建立一个位图表,就能实现在O(1)的时间复杂度上查找到目标结果。

该算法已经经过测试程序对比,输入所有可能的输入,得到的结果与之前的位图算法相同。

当前拉取/合并请求的状态 Intent for your PR

必须选择一项 Choose one (Mandatory):

  • 本拉取/合并请求是一个草稿版本 This PR is for a code-review and is intended to get feedback
  • 本拉取/合并请求是一个成熟版本 This PR is mature, and ready to be integrated into the repo

代码质量 Code Quality:

我在这个拉取/合并请求中已经考虑了 As part of this pull request, I've considered the following:

  • 已经仔细查看过代码改动的对比 Already check the difference between PR and old code
  • 代码风格正确,包括缩进空格,命名及其他风格 Style guide is adhered to, including spacing, naming and other styles
  • 没有垃圾代码,代码尽量精简,不包含#if 0代码,不包含已经被注释了的代码 All redundant code is removed and cleaned up
  • 所有变更均有原因及合理的,并且不会影响到其他软件组件代码或BSP All modifications are justified and not affect other components or BSP
  • 对难懂代码均提供对应的注释 I've commented appropriately where code is tricky
  • 本拉取/合并请求代码是高质量的 Code in this PR is of high quality
  • 本拉取/合并使用formatting等源码格式化工具确保格式符合RT-Thread代码规范 This PR complies with RT-Thread code specification

@Eureka1024
Copy link
Contributor Author

增加一种新的查找字节最低非0位的算法

@enkiller
Copy link
Contributor

运行效率上有没有啥差距

@Eureka1024
Copy link
Contributor Author

Eureka1024 commented Sep 27, 2021

运行效率上有没有啥差距

首先该方式能够省下很多空间,原来的算法需要256个字节,而现在只需要37个字节的空间。
然后,该操作方式没有使用判断之类的语句,只需要常规的运算符实现即可,肯定比原来的效率高一点,具体高多少,我目前不知道有哪些测试能够有效的得出结论,希望有人能指点一下。

我觉得更重要的是,当该系统使用64位的MPU或者CPU的时候,这种方法绝对效果显著,无论时间还是空间效率,都将大大提高。

@Guozhanxin
Copy link
Member

运行效率上有没有啥差距

首先该方式能够省下很多空间,原来的算法需要256个字节,而现在只需要37个字节的空间。 然后,该操作方式没有使用判断之类的语句,只需要常规的运算符实现即可,肯定比原来的效率高一点,具体高多少,我目前不知道有哪些测试能够有效的得出结论,希望有人能指点一下。

我觉得更重要的是,当该系统使用64位的MPU或者CPU的时候,这种方法绝对效果显著,无论时间还是空间效率,都将大大提高。

写个循环调用的函数试试吧。两个函数都运行个多少万次之后,看看执行时间。

我是感觉之前的方式,只有移位和位运算对于计算机处理会方便不少。新加的算法有指数运算和取模运算,感觉时间上可能会慢一点。

@Eureka1024
Copy link
Contributor Author

Eureka1024 commented Oct 28, 2021

运行效率上有没有啥差距

首先该方式能够省下很多空间,原来的算法需要256个字节,而现在只需要37个字节的空间。 然后,该操作方式没有使用判断之类的语句,只需要常规的运算符实现即可,肯定比原来的效率高一点,具体高多少,我目前不知道有哪些测试能够有效的得出结论,希望有人能指点一下。
我觉得更重要的是,当该系统使用64位的MPU或者CPU的时候,这种方法绝对效果显著,无论时间还是空间效率,都将大大提高。

写个循环调用的函数试试吧。两个函数都运行个多少万次之后,看看执行时间。

我是感觉之前的方式,只有移位和位运算对于计算机处理会方便不少。新加的算法有指数运算和取模运算,感觉时间上可能会慢一点。

首先,新加的算法没有指数运算,那是异或操作。
取模操作在一些支持乘除法指令的单片机中,可以很方便的实现。
下面是该算法在 cortex-M3 的汇编实现:

127: int __rt_ffs(int value) 
128: { 
0x0800122C 4601      MOV      r1,r0 ;将r0的值(函数参数值)放入r1中
   130:   return __lowest_bit_bitmap_new[(uint32_t)( value & (value - 1) ^ value ) % 37]; 
0x0800122E 1E48      SUBS     r0,r1,#1 ; (value-1)结果放入r0
0x08001230 4008      ANDS     r0,r0,r1 ;(value & (value-1) 结果放入r0
0x08001232 4048      EORS     r0,r0,r1 ; (value & (value-1) ^ value))实现异或的结果放入r0
0x08001234 2225      MOVS     r2,#0x25 ; 将37放入r2
0x08001236 FBB0F3F2  UDIV     r3,r0,r2 ;r3为商
0x0800123A FB020013  MLS      r0,r2,r3,r0; 乘加,r0 = r0 - r2*r3
0x0800123E 4A01      LDR      r2,[pc,#4] ; @0x08001244 ;找到查找表的位置
0x08001240 5C10      LDRB     r0,[r2,r0] ;得到查找表中的值
   131: } 
0x08001242 4770      BX       lr ;函数返回

而原先的代码则需要比较多的代码实现,由于代码较多,请看这篇文章:https://blog.csdn.net/m0_37697335/article/details/121026018
但是整体而言,从运行时两者的汇编指令数来看,两者的运行效率应该差不多,之后具体跑跑验证。

@enkiller
Copy link
Contributor

enkiller commented Nov 6, 2021

执行效率

  • 主线版本:最少 9 条,最多 13 条。如果优先级均匀分布在 0 - 31,平均就是 11 指令。( 其中一个判定条件 2 条指令,一次计算 4 条指令。)

  • 精简版本:10 条汇编指令,其中有一次除法运算(2-12)时钟周期,平均约为 17 个指令

理论上精简版本性能会差一点

资源占用

  • 主线版本:表格 256 字节,代码 72 字节,总共 328 字节

  • 精简版本:表格 37 字节,代码 24 字节,总共 61 字节

优化 267 字节

CM3 指令周期

[CM3 指令时钟周期] https://developer.arm.com/documentation/ddi0337/h/programmers-model/instruction-set-summary/cortex-m3-instructions

@Eureka1024 Eureka1024 changed the title 增加一种新的查找字节最低非0位的算法 [kernel]增加一种新的查找字节最低非0位的算法 Nov 7, 2021
@Eureka1024
Copy link
Contributor Author

Eureka1024 commented Nov 7, 2021

执行效率

  • 主线版本:最少 9 条,最多 13 条。如果优先级均匀分布在 0 - 31,平均就是 11 指令。( 其中一个判定条件 2 条指令,一次计算 4 条指令。)
  • 精简版本:10 条汇编指令,其中有一次除法运算(2-12)时钟周期,平均约为 17 个指令

理论上精简版本性能会差一点

我使用 STM32F103 来实际测试,就是对所有可能的输入测试(0x00 - 0xFFFFFFFF),测得运行时间为
主线版本耗时:2510470ms,精简版本耗时:2272475ms。发现精简版本的运行效率更高。
分析了下主线版本的汇编代码,发现部分与运算的实现并不能简单实现,应该是造成效率比较慢的原因。如下所示:

1293:     if (value & 0xff00) 
0x08006F8E F401407F  AND      r0,r1,#0xFF00 //与操作
0x08006F92 B128      CBZ      r0,0x08006FA0

写了一篇验证文章,如有错误或者不严谨的地方,麻烦指教。
验证:为RT-Thread内核增加一种新的查找字节最低非0位的算法

@Eureka1024
Copy link
Contributor Author

写了一篇验证文章,主要是验证的一些过程、数据和方法。
如有错误或者不严谨的地方,麻烦指教。
验证:为RT-Thread内核增加一种新的查找字节最低非0位的算法

@enkiller
Copy link
Contributor

enkiller commented Nov 8, 2021

写了一篇验证文章,主要是验证的一些过程、数据和方法。 如有错误或者不严谨的地方,麻烦指教。 验证:为RT-Thread内核增加一种新的查找字节最低非0位的算法

跳转指令确实不能在一个机器周期内完成,当有多次跳转时,耗时可能比除法还要久。从目前的分析情况来看,这种算法,确实很优秀。

@Guozhanxin
Copy link
Member

写了一篇验证文章,主要是验证的一些过程、数据和方法。 如有错误或者不严谨的地方,麻烦指教。 验证:为RT-Thread内核增加一种新的查找字节最低非0位的算法

跳转指令确实不能在一个机器周期内完成,当有多次跳转时,耗时可能比除法还要久。从目前的分析情况来看,这种算法,确实很优秀。

如果感觉没问题的话,请+1

@Guozhanxin Guozhanxin added the +1 Agree +1 label Nov 8, 2021
src/Kconfig Outdated Show resolved Hide resolved
@BernardXiong
Copy link
Member

执行效率

  • 主线版本:最少 9 条,最多 13 条。如果优先级均匀分布在 0 - 31,平均就是 11 指令。( 其中一个判定条件 2 条指令,一次计算 4 条指令。)
  • 精简版本:10 条汇编指令,其中有一次除法运算(2-12)时钟周期,平均约为 17 个指令

理论上精简版本性能会差一点

我使用 STM32F103 来实际测试,就是对所有可能的输入测试(0x00 - 0xFFFFFFFF),测得运行时间为 主线版本耗时:2510470ms,精简版本耗时:2272475ms。发现精简版本的运行效率更高。 分析了下主线版本的汇编代码,发现部分与运算的实现并不能简单实现,应该是造成效率比较慢的原因。如下所示:

1293:     if (value & 0xff00) 
0x08006F8E F401407F  AND      r0,r1,#0xFF00 //与操作
0x08006F92 B128      CBZ      r0,0x08006FA0

写了一篇验证文章,如有错误或者不严谨的地方,麻烦指教。 验证:为RT-Thread内核增加一种新的查找字节最低非0位的算法

欢迎提供更优秀的算法、代码 👍

@BernardXiong BernardXiong merged commit 301856a into RT-Thread:master Nov 11, 2021
@pegasusplus
Copy link

在微信文章上看到了,很棒!留了言似乎还能改进,不用额外空间来折半查找计算最低位bit,我这两天来试试看。

@pegasusplus
Copy link

pegasusplus commented Nov 30, 2024

#9729 我写的查找32bit最低非0bit位置的算法如下:

int __rt_ffs(uint32_t value) {
    if (value == 0)
        return 0; // 0 means no bit 1

    int position = 1; // position start from 1

    // search half range
    if ((value & 0xFFFF) == 0) { // is lower 16bit 0
        position += 16;
        value >>= 16;
    }
    if ((value & 0xFF) == 0) { // is lower 8bit 0
        position += 8;
        value >>= 8;
    }
    if ((value & 0xF) == 0) { // is lower 4bit 0
        position += 4;
        value >>= 4;
    }
    if ((value & 0x3) == 0) { // is lower 2bit 0
        position += 2;
        value >>= 2;
    }
    if ((value & 0x1) == 0) { // is lower 1bit 0
        position += 1;
    }

    return position;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
+1 Agree +1
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants