百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

单片机上跑Python慢?可以这样优化一下速度(二)

bigegpt 2024-09-05 12:14 15 浏览

上接 单片机上跑Python慢?可以这样优化一下速度(一) ,本节接着介绍...

MicroPython代码优化

1> const()声明

MicroPython提供了const()声明的语法,其工作方式类似于C语言中的#define,当代码编译为字节码时,编译器会用数字常量值代替该标识符。这能够避免在代码运行时再进行字典查找,从而提升效率。const()的参数能够为任何在编译时等效为整数的数据,比如0x100或者1<<8等等。

2> 缓存对象引用

当函数或方法重复访问某对象时,可以通过将对象缓存在局部变量中来提高性能:

class foo(object):
    def __init__(self):
        self.ba = bytearray(100)
    def bar(self, obj_display):
        ba_ref = self.ba
        fb = obj_display.framebuffer
        # iterative code using these two objects

如上代码可以降低在bar()方法中重复查询self.ba和obj_display.framebuffer的必要性。

3> 控制垃圾回收

当需要内存分配时,MicroPython会尝试在堆中找到一个足够大的内存块。该过程可能会失败,通常是由于在堆内存中堆满了不再被代码引用的对象。如果确实失败了,垃圾回收进程会回收这些冗余对象所使用的内存,然后再次尝试进行分配——该过程可能要耗费几毫秒。

那么,通过定期调用gc.collect()则会带来一些好处。首先,在实际需要之前即进行收集要快得多——如果频繁进行该过程,其通常耗费1毫秒左右。其次,这样你便可以确定垃圾回收在代码中执行的时间点,而不是等到某个随机时间点再执行,而需要更长的延迟(可能在速度关键的代码部分)。最后,定期执行垃圾回收可以减少堆中的碎片,严重的碎片可能会导致不可恢复的内存分配失败。

本地代码生成器

这使得MicroPython编译器能够直接生成本地CPU操作码,而不是字节码。该特性可涵盖MicroPython的大部分函数,因此多数函数都不需要修改即可得到支持。其通过函数修饰器来使用:

@micropython.native
def foo(self, arg):
    buf = self.linebuf # Cached object
    # code

在当前版本的本地代码生成器中还存在着些许限制:

  • 不支持上下文管理器(with语句);
  • 不支持迭代生成器(Generator);
  • 如果使用了raise语法,则必须提供参数。

本地码执行速度大约是字节码速度的两倍,而性能提高的代价是编译代码大小的增加。

Viper代码生成器

上面讨论的本地代码可与标准Python代码兼容,但接下来的Viper代码却不能与其完全兼容。为了追求性能,MicroPython支持特定的Viper本地数据类型,但整数类型是不兼容的,因为它使用的是机器字:32位硬件上的算术运算是以2**32为模进行的。

像本地生成器一样,Viper也生成机器指令,但其执行了进一步的优化,从而大大提高了性能,特别是整数运算和位操作。其通过如下函数修饰器来使用:

@micropython.viper
def foo(self, arg: int) -> int:
    # 代码...

像上面代码段所示这样来使用python类型提示以帮助Viper优化器是有好处的。类型提示可以提供有关参数和返回值的数据类型信息,其在标准python语言特性里有定义,具体可参见PEP0484。Viper支持其自己的类型集合,比如int,uint(无符号整数),ptr,ptr8,ptr16和ptr32。关于ptrX类型,下面会再讨论。目前,uint类型只服务于一个目的:作为函数返回值的类型提示。如果这样的一个函数返回了0xffffffff,python会将该结果解释为2**32-1,而不是-1。

除了本地生成器的限制外,Viper生成器还有如下使用限制:

  • 函数最多可以有四个参数;
  • 不允许使用默认参数值;
  • 也可以使用浮点数,但没有相应优化行为。

Viper提供了指针类型以协助优化器,具体包括:

  • ptr 对象指针
  • ptr8 字节指针
  • ptr16 16位半字指针
  • ptr32 32位机器字指针

指针的概念可能对于python程序员而言不太熟悉,其与python memoryview对象的相似之处在于:它提供了对存储在内存中数据的直接访问方法。数据项可以使用下标进行访问,但不支持切片:指针只能返回单个数据项。其目的是提供对存储在连续内存位置的数据的快速随机访问,比如存储在支持缓冲协议的对象中的数据,以及微控制器中的内存映射寄存器。值得注意的是,使用指针编程是非常危险的:其不执行边界检查,编译器也不采取任何措施来防止缓冲区溢出错误。

其典型用法是缓存变量:

@micropython.viper
def foo(self, arg: int) -> int:
    buf = ptr8(self.linebuf) # self.linebuf 为bytearray或bytes对象
    for x in range(20, 30):
        bar = buf[x] # Access a data item through the pointer
        # code omitted

在如上实例中,编译器"知道"buf是字节数组的地址,其可以生成相应代码以在运行时快速计算buf[x]的地址。当需要将普通对象转换为Viper本地类型时,应该在函数开始处,而不是在关键的定时循环处执行,因为这种转换可能需要耗费几微妙的时间。转换规则如下:

  • 目前转换操作符有:int,bool,uint,ptr,ptr8,ptr16和ptr32;
  • 转换的结果为本地Viper变量;
  • 转换操作的参数可以为python对象或本地Viper变量;
  • 如果参数已经是本地Viper变量,转换操作并无实质动作,其仅仅改变了类型(比如从uint转换为ptr8),以至于你能使用指针来存取数据;
  • 如果参数为python对象而转换类型为int或uint,那么python对象必须为整数类型,该操作会返回其对应整数值;
  • bool强制转换的参数必须是整数类型(布尔值或整数);当其用作返回类型时,Viper函数会返回True或False对象。
  • 如果参数为python对象,强制转换为ptr,ptr8,ptr16或ptr32,则python对象要么必须有缓冲协议(该情况下返回指向缓冲区指针),要么必须是整数类型(该情况下返回该整数对象的值)。

写入指向只读对象的指针将导致未定义行为。

以下示例说明了如何使用ptr16转换来翻转X1引脚状态n次:

BIT0 = const(1)
@micropython.viper
def toggle_n(n: int):
    odr = ptr16(stm.GPIOA + stm.GPIO_ODR)
    for _ in range(n):
        odr[0] ^= BIT0

直接硬件访问

注意:本节所用代码示例适用于Pyboard,但所述技巧也可用于其它的移植版本。

直接硬件访问属于更高级的编程范畴,其涉及到所用单片机的一些基础知识。考虑如下在Pyboard上切换输出引脚状态的示例代码。其标准写法为:

mypin.value(mypin.value() ^ 1) # mypin 为实例化的输出引脚

这涉及对Pin实例对象value()方法的两次调用开销。这种开销可以通过对芯片GPIO端口数据寄存器(odr)的对应位域执行读/写操作来消除。为了便于实现,stm模块提供了一组常数来对应相关寄存器的地址。快速翻转P4引脚(CPU引脚A14)——对应于绿色LED——能够采用如下方式进行:

import machine
import stm

BIT14 = const(1 << 14)
machine.mem16[stm.GPIOA + stm.GPIO_ODR] ^= BIT14

注, 本篇完结,欢迎关注哦...

相关推荐

或者这些Joplin插件也可以帮助你的笔记应用再一次强大

写在前面距离上次分享《搭建私有全平台多端同步笔记,群晖NAS自建JoplinServer服务》已过去一段时间,大家是否开始使用起来了呢?如果你和我一样已经使用过Joplin有一段时间了,那或许你也会...

Three.JS教程4 threejs中的辅助类

一、辅助类简介Three.js提供了一些辅助类(Helpers)以帮助我们更容易地调试、可视化场景中的元素。ArrowHelepr:创建箭头辅助器;AxisHelper:创建坐标轴辅助器;BoxH...

第2章 还记得点、线、面吗(二)(第二章还能敲钟吗)

glbgltf模型(webvrmodel)-gltf模型下载定制,glb模型下载定制,三维项目电商网站在线三维展示,usdz格式,vr模型网,网页VR模型下载,三维模型下载,webgl网页模型下载我...

如何检查Linux系统硬件信息?从CPU到显卡,一网打尽!

你可能会问:“我为什么要关心硬件信息?”答案很简单:硬件是Linux系统的根基,了解它可以帮你解决很多实际问题。比如:性能调优:知道CPU核心数和内存大小,才能更好地调整程序运行参数。故障排查:系统卡...

SpriteJS:图形库造轮子的那些事儿

从2017年到2020年,我花了大约4年的时间,从零到一,实现了一个可切换WebGL和Canvas2D渲染的,跨平台支持浏览器、SSR、小程序,基于DOM结构和支持响应式的,高...

平时积累的FPGA知识点(6)(fpga经典应用100例)

平时在FPGA群聊等积累的FPGA知识点,第六期:1万兆网接口,发三十万包,会出现掉几包的情况,为什么?原因:没做时钟约束,万兆网接口的实现,本质上都是高速serdes,用IP的话,IP会自带约束。...

芯片逻辑调度框架设计 都需要那些那些软件工具

设计芯片逻辑调度框架通常需要使用以下软件工具:1.逻辑设计工具:例如Vivado、Quartus、SynopsysDesignCompiler等,用于设计和实现逻辑电路。2.仿真工具:例如Mo...

ZYNQ与DSP之间EMIF16通信(正点原子领航者zynq之fpga开发指南v3)

本文主要介绍说明XQ6657Z35-EVM高速数据处理评估板ZYNQ与DSP之间EMIF16通信的功能、使用步骤以及各个例程的运行效果。[基于TIKeyStone架构C6000系列TMS320C6...

好课推荐:从零开始大战FPGA(从零开始的冒险4399)

从零开始大战FPGA引子:本课程为“从零开始大战FPGA”系列课程的基础篇。课程通俗易懂、逻辑性强、示例丰富,课程中尤其强调在设计过程中对“时序”和“逻辑”的把控,以及硬件描述语言与硬件电路相对应的“...

业界第一个真正意义上开源100 Gbps NIC Corundum介绍

来源:内容由「网络交换FPGA」编译自「FCCM2020」,谢谢。FCCM2020在5月4日开始线上举行,对外免费。我们有幸聆听了其中一个有关100G开源NIC的介绍,我们对该文章进行了翻译,并对其中...

高层次综合:解锁FPGA广阔应用的最后一块拼图

我们为什么需要高层次综合高层次综合(High-levelSynthesis)简称HLS,指的是将高层次语言描述的逻辑结构,自动转换成低抽象级语言描述的电路模型的过程。所谓的高层次语言,包括C、C++...

Xilinx文档编号及其内容索引(部分)

Xilinx文档的数量非常多。即使全职从事FPGA相关工作,没有几年时间不可能对器件特性、应用、注意事项等等有较为全面的了解。本文记录了我自使用Xilinx系列FPGA以来或精读、或翻阅、或查询过的文...

Xilinx Vivado联合Modelsim软件仿真

引言:Xilinx公司Vivado开发软件自带仿真工具,可以实现一般性能的FPGA软件仿真测试,其测试执行效率以及性能都不如第三方专用仿真软件Modelsim强。本文我们介绍下如何进行Vivado20...

体育动画直播是怎么做出来的?从数据到虚拟赛场的科技魔法!

你是否见过这样的比赛直播?没有真实球员,却能看梅西带球突破?足球比赛变成动画版,但数据100%真实?电竞比赛用虚拟形象直播,选手操作实时同步?这就是体育动画直播——一种融合实时数据、游戏引擎和AI的...

Dialogue between CPC and political parties of neighboring countries held in Beijing

BEIJING,May26(Xinhua)--TheCommunistPartyofChina(CPC)inDialoguewithPoliticalPartiesof...