比较Raspberry Pi上用Python编写的算法与STM32上用C编写的算法[已关闭]

bvpmtnay  于 2022-12-03  发布在  Python
关注(0)|答案(2)|浏览(156)

已关闭。此问题需要details or clarity。当前不接受答案。
**想要改进此问题吗?**通过editing this post添加详细信息并阐明问题。

12天前关闭。
Improve this question
英语不是我的第一语言,所以很抱歉我写的不好。
我需要优化一个算法,它是用Python编写的,运行在树莓派上。线索是,我需要编写优化的代码作为一个C程序运行在stm32f4上。
这是一个图像处理算法(我知道,在微控制器上用C语言进行图像处理听起来很有趣...),功能必须保持不变(所以输出相同,但有公差)。当然,我需要一种方法来对两个程序进行基准测试。
在我的情况下,“优化”意味着程序应该运行得更快(这是自动的,但我需要表明,它是更快的,因为优化的代码,而不是因为它是用C写的,并在裸机系统上运行)。
我知道,例如,我可以比较代码行数,因为行数越少,程序运行速度越快。是否有更多的“因素”,这些因素与系统无关,我可以通过比较来解释为什么优化后的代码运行速度更快?
此致,丹
PS:我考虑过用cython把Python代码转换成C代码。然后我可以编译它,比较汇编代码或机器代码。但是我不确定这是不是正确的方法,因为我不知道cython到底在做什么。

wlzqhblo

wlzqhblo1#

当然,我需要一种方法来衡量这两个程序。
对于嵌入式系统,这 * 总是 * 通过在算法开始和结束时切换GPIO引脚,然后用示波器测量时间来完成。这在Rasp PI和STM32目标上都应该是可能的。但您将测量原始执行速度,而不仅仅是算法- Rasp PI将与上下文切换等混淆。
我知道,例如,我可以比较代码行数,因为行数越少,程序越快。
不,这是无稽之谈。行数并不一定与执行速度有任何关系。如果你这样认为,那么我会说你还太缺乏经验,无法为特定目标进行手动代码优化。
至于具体的性能提升,放弃Linux而选择裸机将给予巨大的性能提升。另一方面,你将同时从一些Cortex A缩小到M4,后者运行在更低的时钟和缺乏缓存。但这也意味着,如果你让它在M4上运行得更快,那就完成了使命。因为它是一个功能较弱的目标。(对于裸机Cortex M4来说,超越Linux PC应该是轻而易举的事。)
我怀疑仅仅从Python转换到C语言就能大大提高性能,因为Python中“幕后”执行的所有类型泛型goo和隐式/隐藏函数调用都将被删除。
除此之外,STM32 F4非常先进,具有分支预测功能,并且还具有FPU。因此,您仍然可以考虑减少分支和浮点操作的数量。您还可以查看CPU时钟使用与闪存等待状态,看看是否有可能的改进。据我所知,该MCU没有数据缓存,这意味着它无法补偿闪存等待状态。因此,如果等待状态是一个瓶颈,可以考虑在RAM中执行代码。或者尽可能简单地为它计时。

pnwntuvh

pnwntuvh2#

更少的代码行、更少的机器指令并不意味着更快。

void more_fun ( unsigned int );
void fun ( void )
{
    more_fun(0x12345678);
}

00000000 <fun>:
   0:   4801        ldr r0, [pc, #4]    ; (8 <fun+0x8>)
   2:   f7ff bffe   b.w 0 <more_fun>
   6:   bf00        nop
   8:   12345678    .word   0x12345678

这是一个功能完美的解决方案,但是

.thumb
.cpu cortex-m4
.syntax unified

movw r0,0x5678
movt r0,0x1234

ldr r1,=0x12345678

Disassembly of section .text:

00000000 <.text>:
   0:   f245 6078   movw    r0, #22136  ; 0x5678
   4:   f2c1 2034   movt    r0, #4660   ; 0x1234
   8:   4900        ldr r1, [pc, #0]    ; (c <.text+0xc>)
   a:   0000        .short  0x0000
   c:   12345678    .word   0x12345678

它是通用的。一个movw/movt会得到同样的结果,但是用两个指令而不是一个。那应该是慢的两倍,是吗?一点也不,ldr是一个负载,处理器停止等待一个存储器周期的发生,即使在零等待状态存储器的情况下,该存储器周期也需要一定数量的时钟来产生。这些微处理器上的闪存即使有预取器和缓存,仍然比处理器慢2倍或4倍以上。
在你的cortex-a操作系统和dram上,可能需要几十到几百个时钟周期才能恢复这些数据,这取决于情况。即使是在一级缓存中,它仍然没有那么快。
而movw/movt是两倍多的指令。它们是线性的,被送入流水线,流水线不必为它们中的任何一个停顿,停顿是不确定的。现在在循环中(或不)我们可能到达缓存边界的位置(如果您甚至具有iCache)高速缓存线填充将花费一段时间且额外指令可能导致关于边界在哪里的一些坏运气,但是,如果您要推动这种手动调优,您必须了解所有这些内容,而且,正如我在这里和其他地方多次演示的那样,代码对齐可能很重要,特别是对于ARM等高性能内核。因此,在这里添加NOP可能会显著提高循环的性能,从而获得整体优势,仅从提取效果来看,然后再加上编译器无法知道目标系统,也无法知道哪种方式更好地实现某个东西。
我认为另一个答案是好的。Python到C,是的,你应该看到一个即时的改进。但它是不确定的,你经常会发现,尽管障碍/膨胀/等,Linux上的应用程序可能会超过运行完全相同的C源代码的MCU,因为你在C级别优化它。但你的cortex-a可能运行缓慢,你的cortex-m运行快速。
我认为arm将0x 00000000到0x 20000000 -1、0x 20000000 -1和0x 40000000的规则放在某个地方的原因是,他们可以在没有MMU的情况下打开数据缓存。也许这只是cortex-m7而不是m4。我忘记了cortex-ms上的缓存细节,因为我不使用该高速缓存。
st在stm32 s中有一个闪存缓存,通常无法关闭(并可以帮助或伤害你的基准,记住所有的基准都是废话,很容易使一个缓慢的系统看起来比一个快速的系统更快,等等),我认为一个预取器以及在前面的闪存,以便可以帮助。时钟的MCU了尽可能快,你可以让核心运行得更快,可以使闪存等待状态更长,使得该事情主要依赖于高速缓存而不是依赖于处理器,然而,sram应该按比例增加,因此对于相同对齐上的相同机器代码,从sram运行的代码可以快2到几倍。其他公司确实有这种高速缓存,但是可能仍然有预取器来使线性代码运行得更快(使movw/movt可能比随机访问ldr更快,因为ldr可能会使管道停止)
1.将代码转换为C
1.使其至少运行并给予与python相同的结果
1.看看你能不能把它装进MCU
1.使其给予与主机上相同的结果
1.以对准、分支预测(在核心中可能默认为开启)、iCache(如果存在话)等开始。
1.然后试着改变C代码
1.我做了最后的努力,试图调整/优化编译器的程序集输出
gpio方法增加了测试的时间,如果你做了一个高级别的c gpio调用来做它,可能会把你的结果弄乱,你需要试着在测试代码之前和之后的单个指令中做它。
mcu中的定时器会非常好地工作,因为它通常是在cpu时钟下工作的。因此,如果你在asm或单个指令中执行采样,如果你对定时器或gpio使用time()调用或库调用,它会/将扭曲/破坏你的结果,并会给你留下混乱或虚假的结果(基准是bxxxsxxx),那么它是在采样前后执行采样所花费的时间加上一个时钟或几个时钟。

相关问题