这个优化例子来自 Damien在 pycomau 上的演讲使用MicroPython高效快速编程,演讲视频大家可以在前几天的订阅号中查看。
首先我们看下面的程序,它在循环中翻转LED,然后通过运行的时间和翻转次数,计算出每秒翻转的频率。
frommachineimportPin
importtime
led=Pin('A13')
N=200000
t0=time.ticks_us()
foriinrange(N):
led.on()
led.off()
t1=time.ticks_us()
dt=time.ticks_diff(t1,t0)
fmt='{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
print(fmt.format(dt*1e-6,dt/N,N/dt*1e3))
我们将这段代码保存为文件led1.py,然后import led1执行。在pybv10或者pyboardCN上结果是:
3.381sec,16.905usec/blink:59.16kblink/sec
在MicroPython程序优化原则中,提到尽量在程序中执行功能,不要在主程序中运行,因此可以将LED翻转放在函数中执行。
frommachineimportPin
importtime
led=Pin('A13')
N=200000
defblink_simple(n):
foriinrange(n):
led.on()
led.off()
deftime_it(f,n):
t0=time.ticks_us()
f(n)
t1=time.ticks_us()
dt=time.ticks_diff(t1,t0)
fmt='{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
print(fmt.format(dt*1e-6,dt/n,n/dt*1e3))
time_it(blink_simple,N)
运行后的结果是:
2.902 sec, 14.509 usec/blink : 68.92 kblink/sec
可以看到,我们没有做什么实质的改到,就明显提高了速度。
循环是最消耗运行时间的,我们对循环中led.on()和led.off()两个动作进行优化,将它们预先载入内存,而无需循环中每次载入。
frommachineimportPin
importtime
led=Pin('A13')
N=200000
defblink_simple(n):
on=led.on
off=led.off
foriinrange(n):
on()
off()
deftime_it(f,n):
t0=time.ticks_us()
f(n)
t1=time.ticks_us()
dt=time.ticks_diff(t1,t0)
fmt='{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
print(fmt.format(dt*1e-6,dt/n,n/dt*1e3))
time_it(blink_simple,N)
优化后结果是
1.617 sec, 8.086 usec/blink : 123.68 kblink/sec
提高了将近一倍。
进一步将循环中对 range(n) 也进行优化
frommachineimportPin
importtime
led=Pin('A13')
N=200000
defblink_simple(n):
on=led.on
off=led.off
r=range(n)
foriinr:
on()
off()
deftime_it(f,n):
t0=time.ticks_us()
f(n)
t1=time.ticks_us()
dt=time.ticks_diff(t1,t0)
fmt='{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
print(fmt.format(dt*1e-6,dt/n,n/dt*1e3))
time_it(blink_simple,N)
运行结果是
1.121 sec, 5.607 usec/blink : 178.35 kblink/sec
再次提升了性能。
进一步对循环中的操作优化,减少循环次数
frommachineimportPin
importtime
led=Pin('A13')
N=200000
defblink_simple(n):
n//=8
on=led.on
off=led.off
r=range(n)
foriinr:
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
deftime_it(f,n):
t0=time.ticks_us()
f(n)
t1=time.ticks_us()
dt=time.ticks_diff(t1,t0)
fmt='{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
print(fmt.format(dt*1e-6,dt/n,n/dt*1e3))
time_it(blink_simple,N)
速度又有明显提升。
0.913 sec, 4.563 usec/blink : 219.16 kblink/sec
根据MicroPython的优化功能,可以将程序声明为native code(本地代码),它使用CPU的操作码(opcode),而不是字节码(bytecode)
frommachineimportPin
importtime
led=Pin('A13')
N=200000
@micropython.native
defblink_simple(n):
n//=8
on=led.on
off=led.off
r=range(n)
foriinr:
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
on()
off()
deftime_it(f,n):
t0=time.ticks_us()
f(n)
t1=time.ticks_us()
dt=time.ticks_diff(t1,t0)
fmt='{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
print(fmt.format(dt*1e-6,dt/n,n/dt*1e3))
time_it(blink_simple,N)
测试结果:
0.704 sec, 3.521 usec/blink : 284.00 kblink/sec
程序还可以进一步优化,完整内容请参看下方的阅读原文,获取终极优化的方法。