注册 登录
电子工程世界-论坛 返回首页 EEWORLD首页 频道 EE大学堂 下载中心 Datasheet 专题

dcexpert的个人空间 http://home.eeworld.com.cn/space-uid-573537.html [收藏] [复制] [分享] [RSS]

日志

micropython程序优化实例

已有 157 次阅读2018-9-6 13:55 |个人分类:MicroPython

这个优化例子来自 Damien 在 pycomau 上的演讲使用MicroPython高效快速编程

首先我们看下面的程序,它在循环中翻转LED,然后通过运行的时间和翻转次数,计算出每秒翻转的频率。

  1. from machine import Pin
  2. import time

  3. led = Pin('A13')
  4. N = 200000

  5. t0 = time.ticks_us()

  6. for i in range(N):
  7.         led.on()
  8.         led.off()

  9. t1 = time.ticks_us()
  10. dt = time.ticks_diff(t1, t0)
  11. fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  12. print(fmt.format(dt * 1e-6, dt / N, N / dt * 1e3))
复制代码

我们将这段代码保存为文件led1.py,然后import led1执行。在pybv10或者pyboardCN上结果是:
  1. 3.381 sec, 16.905 usec/blink :    59.16 kblink/sec
复制代码

[page]

MicroPython程序优化原则 中,提到尽量在程序中执行功能,不要在主程序中运行,因此可以将LED翻转放在函数中执行。

  1. from machine import Pin
  2. import time

  3. led = Pin('A13')
  4. N = 200000

  5. def blink_simple(n):
  6.         for i in range(n):
  7.                 led.on()
  8.                 led.off()

  9. def time_it(f, n):
  10.         t0 = time.ticks_us()
  11.         f(n)
  12.         t1 = time.ticks_us()
  13.         dt = time.ticks_diff(t1, t0)
  14.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  15.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  16. time_it(blink_simple, N)
复制代码

运行后的结果是:
  1. 2.902 sec, 14.509 usec/blink :    68.92 kblink/sec
复制代码

可以看到,我们没有做什么实质的改到,就明显提高了速度。

[page]
循环是最消耗运行时间的,我们对循环中led.on()和led.off()两个动作进行优化,将它们预先载入内存,而无需循环中每次载入。
  1. from machine import Pin
  2. import time

  3. led = Pin('A13')
  4. N = 200000

  5. def blink_simple(n):
  6.         on = led.on
  7.         off = led.off
  8.         for i in range(n):
  9.                 on()
  10.                 off()

  11. def time_it(f, n):
  12.         t0 = time.ticks_us()
  13.         f(n)
  14.         t1 = time.ticks_us()
  15.         dt = time.ticks_diff(t1, t0)
  16.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  17.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  18. time_it(blink_simple, N)
复制代码

运行结果是
  1. 1.617 sec,  8.086 usec/blink :   123.68 kblink/sec
复制代码

提高了将近一倍。

[page]
进一步将循环中对 range(n) 也进行优化
  1. from machine import Pin
  2. import time

  3. led = Pin('A13')
  4. N = 200000

  5. def blink_simple(n):
  6.         on = led.on
  7.         off = led.off
  8.         r = range(n)
  9.         for i in r:
  10.                 on()
  11.                 off()

  12. def time_it(f, n):
  13.         t0 = time.ticks_us()
  14.         f(n)
  15.         t1 = time.ticks_us()
  16.         dt = time.ticks_diff(t1, t0)
  17.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  18.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  19. time_it(blink_simple, N)
复制代码

运行结果是
  1. 1.121 sec,  5.607 usec/blink :   178.35 kblink/sec
复制代码

效果非常明显。


[page]
进一步对循环中的操作优化,减少循环次数
  1. from machine import Pin
  2. import time

  3. led = Pin('A13')
  4. N = 200000

  5. def blink_simple(n):
  6.         n //= 8
  7.         on = led.on
  8.         off = led.off
  9.         r = range(n)
  10.         for i in r:
  11.                 on()
  12.                 off()
  13.                 on()
  14.                 off()
  15.                 on()
  16.                 off()
  17.                 on()
  18.                 off()
  19.                 on()
  20.                 off()
  21.                 on()
  22.                 off()
  23.                 on()
  24.                 off()
  25.                 on()
  26.                 off()

  27. def time_it(f, n):
  28.         t0 = time.ticks_us()
  29.         f(n)
  30.         t1 = time.ticks_us()
  31.         dt = time.ticks_diff(t1, t0)
  32.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  33.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  34. time_it(blink_simple, N)
复制代码

速度又有明显提升。
  1. 0.913 sec,  4.563 usec/blink :   219.16 kblink/sec
复制代码

[page]
根据MicroPython的优化功能,可以将程序声明为native code(本地代码),它使用CPU的操作码(opcode),而不是字节码(bytecode)
  1. from machine import Pin
  2. import time

  3. led = Pin('A13')
  4. N = 200000

  5. @micropython.native
  6. def blink_simple(n):
  7.         n //= 8
  8.         on = led.on
  9.         off = led.off
  10.         r = range(n)
  11.         for i in r:
  12.                 on()
  13.                 off()
  14.                 on()
  15.                 off()
  16.                 on()
  17.                 off()
  18.                 on()
  19.                 off()
  20.                 on()
  21.                 off()
  22.                 on()
  23.                 off()
  24.                 on()
  25.                 off()
  26.                 on()
  27.                 off()

  28. def time_it(f, n):
  29.         t0 = time.ticks_us()
  30.         f(n)
  31.         t1 = time.ticks_us()
  32.         dt = time.ticks_diff(t1, t0)
  33.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  34.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  35. time_it(blink_simple, N)
复制代码

结果如下
  1. 0.704 sec,  3.521 usec/blink :   284.00 kblink/sec
复制代码

[page]
除了native,还可以使用viper code模式,它进一步提升了整数计算和位操作性能
  1. from machine import Pin
  2. import time, stm

  3. led = Pin('A13')
  4. N = 200000

  5. @micropython.viper
  6. def blink_simple(n:int):
  7.         n //= 8
  8.         p = ptr16(stm.GPIOB + stm.GPIO_BSRR)
  9.         for i in range(n):
  10.                 p[0] = 1 << 4
  11.                 p[1] = 1 << 4
  12.                 p[0] = 1 << 4
  13.                 p[1] = 1 << 4
  14.                 p[0] = 1 << 4
  15.                 p[1] = 1 << 4
  16.                 p[0] = 1 << 4
  17.                 p[1] = 1 << 4
  18.                 p[0] = 1 << 4
  19.                 p[1] = 1 << 4
  20.                 p[0] = 1 << 4
  21.                 p[1] = 1 << 4
  22.                 p[0] = 1 << 4
  23.                 p[1] = 1 << 4
  24.                 p[0] = 1 << 4
  25.                 p[1] = 1 << 4

  26. def time_it(f, n):
  27.         t0 = time.ticks_us()
  28.         f(n)
  29.         t1 = time.ticks_us()
  30.         dt = time.ticks_diff(t1, t0)
  31.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  32.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  33. time_it(blink_simple, N)
复制代码

运行结果的确是大幅提升了性能
  1. 0.016 sec,  0.078 usec/blink : 12879.13 kblink/sec
复制代码


[page]
最终我们还可以通过嵌入汇编方式,最大限度提升性能
  1. from machine import Pin
  2. import time, stm

  3. led = Pin('A13')
  4. N = 200000

  5. @micropython.asm_thumb
  6. def blink_simple(r0):
  7.         lsr(r0, r0, 3)
  8.         movwt(r1, stm.GPIOB + stm.GPIO_BSRR)
  9.         mov(r2, 1 << 4)
  10.         label(loop)
  11.         strh(r2, [r1, 0])
  12.         strh(r2, [r1, 2])
  13.         strh(r2, [r1, 0])
  14.         strh(r2, [r1, 2])
  15.         strh(r2, [r1, 0])
  16.         strh(r2, [r1, 2])
  17.         strh(r2, [r1, 0])
  18.         strh(r2, [r1, 2])
  19.         strh(r2, [r1, 0])
  20.         strh(r2, [r1, 2])
  21.         strh(r2, [r1, 0])
  22.         strh(r2, [r1, 2])
  23.         strh(r2, [r1, 0])
  24.         strh(r2, [r1, 2])
  25.         strh(r2, [r1, 0])
  26.         strh(r2, [r1, 2])
  27.         sub(r0, 1)
  28.         bne(loop)

  29. def time_it(f, n):
  30.         t0 = time.ticks_us()
  31.         f(n)
  32.         t1 = time.ticks_us()
  33.         dt = time.ticks_diff(t1, t0)
  34.         fmt = '{:5.3f} sec, {:6.3f} usec/blink : {:8.2f} kblink/sec'
  35.         print(fmt.format(dt * 1e-6, dt / n, n / dt * 1e3))

  36. time_it(blink_simple, N)
复制代码


运行结果是
  1. 0.007 sec,  0.037 usec/blink : 27322.40 kblink/sec
复制代码

这个结果已经非常接近极限了。


从前面的优化顺序,可以看到我们并没有大幅修改程序,就可以极高程序的性能。实际使用中,大家可以灵活选择,提高程序的性能。

本文来自论坛,点击查看完整帖子内容。

评论 (0 个评论)

facelist doodle 涂鸦板

您需要登录后才可以评论 登录 | 注册

小黑屋|手机版|Archiver|电子工程世界 ( 京ICP证 060456 )

GMT+8, 2018-11-20 05:54 , Processed in 0.015877 second(s), 10 queries , Gzip On, MemCache On.

Powered by EEWORLD电子工程世界

© 2018 http://bbs.eeworld.com.cn/

返回顶部