EAFP/LBYL programming style: which is faster?

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

EAFP/LBYL programming style: which is faster?

Post by pythoncoder » Thu Jan 11, 2018 6:27 am

Python orthodoxy states "Easier to Ask Forgiveness" rather than "Look Before You Leap". For example if you want to look up an item in a dictionary you should simply do it, and trap the KeyError if the item is not present. This Wikpedia reference explains it, and offers good reasons for using it. The observations below aren't intended to contradict this, but do suggest that in some circumstances LBYL can be faster in MicroPython.

From a firmware writer's perspective sometimes every μs matters. So I tested a failed lookup using EAFP against two LBYL approaches on the Pyboard:

Code: Select all

import utime as time
mydict = {'cat':0,'dog':1,'pig':2,'cow':3,'rabbit':4,'bull':5,'ewe':6,'ram':7,'hart':8,'aardvark':9}

t = time.ticks_us()
try:
    res = mydict['hind']
except KeyError:
    res = -1
delta = time.ticks_diff(time.ticks_us(), t)
print('Trap exception: time = {}us result = {}'.format(delta, res))

t = time.ticks_us()
res = mydict.pop('hind', -1)
delta = time.ticks_diff(time.ticks_us(), t)
print('Pop: time = {}us result = {}'.format(delta, res))

t = time.ticks_us()
if 'hind' in mydict:
    res = mydict['hind']
else:
    res = -1
delta = time.ticks_diff(time.ticks_us(), t)
print('Test lookup: time = {}us result = {}'.format(delta, res))
Outcome:

Code: Select all

=== 
Trap exception: time = 62us result = -1
Pop: time = 24us result = -1
Test lookup: time = 21us result = -1
The speed of the last one surprised me as, on the face of it, two lookups are performed. There does seem to be some caching going on, however. I repeated the three tests so that each in turn was performed immediately after a soft reset. The outcome was:
Trap exception: 62μs as above
Pop: 34μs
Test lookup: 32μs
Peter Hinch
Index to my micropython libraries.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: EAFP/LBYL programming style: which is faster?

Post by Roberthh » Thu Jan 11, 2018 8:18 am

The link you stated is very interesting and also tells, that in the 'fail' case EAFP is slower. Did you also try the times for the 'pass' case?
Besides that., I found try/except very elegant in a nested fail/pass situatuation, where you can easily go back to safe ground, and oen of the strenghts of Python.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: EAFP/LBYL programming style: which is faster?

Post by pythoncoder » Thu Jan 11, 2018 9:10 am

Pass times:
Trap exception 32μs
Test lookup 23μs
Pop 22μs
Again I re-ordered the tests to avoid issues with caching: each test was performed immediately after a ctrl-D.

Try-except is indeed one of the great features of Python and I use it extensively. If these tests are anything to go by MicroPython implements it efficiently. I thought it worth testing as I have heard it described as slow.
Peter Hinch
Index to my micropython libraries.

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: EAFP/LBYL programming style: which is faster?

Post by stijn » Thu Jan 11, 2018 10:27 am

This is sort of expected, no? If you look at the code paths followed there's a lot more going one to deal with exceptions. Though I'd expect the differences in CPython to be bigger yet. Not sure though.
Duration of 1000 iterations, repeated 200 times, windows port:
trap min 0.698 max 3.800 avg 0.748 mSec
pop min 0.275 max 0.349 avg 0.287 mSec
lookup min 0.243 max 0.267 avg 0.253 mSec

Edit: the pop() case isn't the same as the others because it modifies the dict, replacing pop() with get() does make that the fastest one. Also when using a 50/50 mix of non-repeated hits and misses randomly sampled but evenly distibuted, the differences become smaller and the trap case becomes only a bit slower than the others.
Trap min 3.720 max 7.279 avg 3.935 mSec
Pop min 3.320 max 6.464 avg 3.504 mSec
Lookup min 3.379 max 6.675 avg 3.554 mSec
Get min 3.264 max 6.489 avg 3.392 mSec

Post Reply