I've been working on a project using Python 2.7 and TKinter which needs to render 25 images per second based on GPIO inputs.

I have working code on my Ubuntu laptop, which is able to render an image every 0.015 seconds which is more than enough. When I put this on my Raspberry Pi 3, an image is rendered in 0.15 seconds which is way to much, the limit being 0.04 seconds.
The fact is that nor my CPU or RAM is used at 100%, so I'm wondering where the limitation could come from, any ideas?

For your information here are a few extra inputs:

  • The images are ~ 100 - 150 kb
  • The format of images is gif so I don't need to use the pillow library
  • I'm only using tkinter
  • I've tried using the memory card and a USB drive to read the images to see if the limitation was coming from the transfer rate of the microSD card

Some of my code to give you a better idea:

import Tkinter as tk
import time

root = tk.Tk()
canvas = tk.Canvas(root, width = 1280, height = 720)
tkImage = tk.PhotoImage(file=img) #img is the path to the image
canvasImage = canvas.create_image(640, 360, image=tkImage) #show first image
dicimg = {}

def update_image(event):
    global i, max, canvas, canvasImage
    start = time.time()
    if i < max:
        i = i + 2
        i = 0
    img = image_spot + 'frame{0:05d}.gif'.format(i)
    tkImage = tk.PhotoImage(file=img)
    dicimg['img1'] = tkImage
    canvas.itemconfig(canvasImage, image = tkImage)
    print('uploaded in: '+ str(time.time()-start))

while i < max:

root.bind("<space>", update_image)

As suggested by James Kent I've profiled my code and this is the result after updating the images 50 times:

Thu Apr 12 14:35:09 2018    profiler

     1932 function calls (1922 primitive calls) in 7.316 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.010    0.010    7.316    7.316 test_tkinter_gif_auto.py:1(<module>)
      206    7.039    0.034    7.039    0.034 {built-in method call}
       50    0.007    0.000    6.948    0.139 test_tkinter_gif_auto.py:20(update_image)
       51    0.001    0.000    3.127    0.061 /usr/lib/python2.7/lib-tk/Tkinter.py:3370(__init__)
       51    0.005    0.000    3.125    0.061 /usr/lib/python2.7/lib-tk/Tkinter.py:3307(__init__)
       50    0.001    0.000    2.927    0.059 /usr/lib/python2.7/lib-tk/Tkinter.py:2405(itemconfigure)
       51    0.002    0.000    2.926    0.057 /usr/lib/python2.7/lib-tk/Tkinter.py:1315(_configure)
       50    0.001    0.000    0.875    0.018 /usr/lib/python2.7/lib-tk/Tkinter.py:1025(update)
        1    0.000    0.000    0.219    0.219 /usr/lib/python2.7/lib-tk/Tkinter.py:1803(__init__)
        1    0.218    0.218    0.218    0.218 {_tkinter.create}
       49    0.001    0.000    0.072    0.001 /usr/lib/python2.7/lib-tk/Tkinter.py:3332(__del__)
        1    0.000    0.000    0.055    0.055 /usr/lib/python2.7/lib-tk/Tkinter.py:2321(create_image)
        1    0.000    0.000    0.055    0.055 /usr/lib/python2.7/lib-tk/Tkinter.py:2304(_create)
        1    0.019    0.019    0.021    0.021 /usr/lib/python2.7/lib-tk/Tkinter.py:31(<module>)
       54    0.002    0.000    0.003    0.000 /usr/lib/python2.7/lib-tk/Tkinter.py:1165(_options)

If I understand this correctly what takes most of the time is the _init_ from the Tkinter PhotoImage and an other _init_ (where??), followed by the itemconfigure and the update. Any idea how to optimize?

  • have you tried profiling your code first? this should help you work out which bit is slow, only then can we look at how to speed it up. docs.python.org/3/library/profile.html#instant-user-s-manual – James Kent Apr 12 '18 at 12:13
  • also, a comment about the CPU, cPython as written above (not using multiprocess) can only utilize a single core, as the pi3 has 4 cores the highest use you would see with a single python script maxing out its core would be 25% (with a bit of variation for system processes) – James Kent Apr 12 '18 at 12:17
  • @JamesKent: I haven't tried profiling my code, I will go read the information on the link you gave me. – No_or_yes Apr 12 '18 at 14:15
  • @JamesKent: The fact Python only uses one core is interesting, because When I launch this on an Intel core i7 all cores seem to work together. Is it possible this is due to Raspbian running on Pi 3 and Ubuntu on the Intel core i7? – No_or_yes Apr 12 '18 at 14:18
  • it shouldn't make any difference what operating system you use, if it's C python (the ref implementation) then it implements something called the GIL or global interpreter lock, this means that even with threading only one thread is active at a time, as a thread can only run on a single cpu core at a time it is by definition limited to one core, other python implementations do not have the GIL, and in these cases multi thread/core is possible. the only thing that could mess with this is native code does not need the GIL, so in some cases (usually I/O) multi core operation may happen – James Kent Apr 12 '18 at 14:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.