Last active
June 15, 2020 16:35
-
-
Save amcgregor/405354 to your computer and use it in GitHub Desktop.
Python timeit benchmarking for realistic optimizations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# All units in usecs (µsec) comparing Python 2.7 | 3.7. | |
# Last updated: 2019-02-11 | |
# MacBook Pro (15-inch, 2016) | |
# macOS 10.14.3 | |
# 2.7 GHz Intel Core i7 | |
# 16 GB 2133 MHz LPDDR3 | |
python -m timeit "200 <= 250 < 300" # 0.0354 | 0.059 | |
python2.7 -m timeit "250 in xrange(200, 300)" # 1.25 | |
python3.7 -m timeit "250 in range(200, 300)" # 0.324 -- yay for __contains__! | |
python -m timeit 's="Content-Type: text/html\r\n"' 'a,b = s.split(":", 1)' # 0.305 | 0.249 | |
python -m timeit 's="Content-Type: text/html\r\n"' 'a,b = s.split(":")' # 0.287 | 0.285 | |
python -m timeit 's="Content-Type: text/html\r\n"' 'a,c = s.partition(":")[::2]' # 0.22 | 0.275 | |
python -m timeit 's="Content-Type: text/html\r\n"' 'a,b,c = s.partition(":")' # 0.13 | 0.189 ! | |
# 3.8.2 | |
python3.8 -m timeit 's="Content-Type: text/html\r\n"' 'a,b,c = s.partition(":")' # 281ns | |
python3.8 -m timeit 'import re; s="Content-Type: text/html\r\n"' 'a, b = re.split(":", s)' # 2µs (2000ns) | |
# Partition is faster, despite the extra variable assignment. | |
python -m timeit 's="Content-Type: text/html\r\n"' 's.upper()' # 0.348 | 0.121 | |
python -m timeit 's="CONTENT-TYPE: text/html\r\n"' 's.upper()' # 0.307 | 0.106 ! | |
python -m timeit 's="CONTENT-TYPE: TEXT/HTML\r\n"' 's.upper()' # 0.270 | 0.101 ! | |
python -m timeit 's="Content-Type: text/html\r\n"' 's.title()' # 0.344 | 0.340 | |
python -m timeit 's="Content-Type: text/html\r\n"' 's.lower()' # 0.274 | 0.102 ! | |
# If you need to case-insensitively compare strings that are mostly lower-case, lower-case them. | |
python -m timeit 'a="foo";b="bar"' '", ".join((a, b))' # 0.136 | 0.158 | |
python -m timeit 'a="foo";b="bar"' 'a + ", " + b' # 0.0668 | 0.135 ! | |
# Join might be fancy for small lists or tuples, but it's a horrible waste of resources. | |
python -m timeit 'a="foo.bz"' 'a.endswith("bz")' # 0.164 | 0.157 | |
python -m timeit 'a="foo.bz"' 'a[-2:] == "bz"' # 0.0676 | 0.132 ! | |
# Slicing is fast, no, faster than that. | |
python -m timeit 'line="GET / HTTP/1.1"' 'a,b,c = line.split(" ", 2)' # 0.304 | 0.274 | |
python -m timeit 'line="GET / HTTP/1.1"' 'a,_,_ = line.partition(" "); b,_,c = _.partition(" ")' # 0.226 | 0.307 ! | |
python -m timeit 'line="GET / HTTP/1.1"' 'a,b,c = line.split(" ")' # 0.295 | 0.228 ! | |
# Splitting is strange; how can limiting the split take -longer- than so much variable assignment? | |
python2.7 -m timeit 'from urllib import unquote; import re; qs = re.compile("(?i)%2F"); path = "/foo%2Fbar/bar/baz"' '[unquote(x) for x in qs.split(path)]' # 4.33 | |
python3.7 -m timeit 'from urllib.parse import unquote; import re; qs = re.compile("(?i)%2F"); path = "/foo%2Fbar/bar/baz"' '[unquote(x) for x in qs.split(path)]' # 3.26 | |
python2.7 -m timeit 'from urllib import unquote; import re; path = "/foo%2Fbar/bar/baz"' 'path.replace("%2F", "\0").replace("%2f", "\0"); path = unquote(path); path.replace("\0", "%2F")' # 3.69 | |
python3.7 -m timeit 'from urllib.parse import unquote; import re; path = "/foo%2Fbar/bar/baz"' 'path.replace("%2F", "\0").replace("%2f", "\0"); path = unquote(path); path.replace("\0", "%2F")' # 5.79 | |
# The decoding of URLs has some interesting requirements. Using regular expressions may be easy... but not fast. | |
python -m timeit 'line=""' 'line.split(",")' # 0.220 | 0.129 | |
python -m timeit 'line=None' 'if line: line.split(",")' # 0.0192 | 0.0193 ! | |
# It is better to test for a value before splitting, rather than splitting empty strings. | |
python -m timeit 'a="://"; b="http://www.example.com/"' 'a in b' # 0.0404 | 0.0484 | |
python -m timeit 'a="://"; b="http://www.example.com/"' 'b.find(a)' # 0.186 | 0.167 | |
python -m timeit 'a="://"; b="/foo/bar/baz"' 'a in b' # 0.0401 | 0.0505 | |
python -m timeit 'a="://"; b="/foo/bar/baz"' 'b.find(a)' # 0.191 | 0.176 | |
# In is faster, but doesn't return the offset. | |
python -m timeit 'uri="/foo/bar/baz"' 'uri.startswith("/")' # 0.174 | 0.174 | |
python3.7 -m timeit 'uri="/foo/bar/baz"' 'uri[0] == "/"' # 0.0467 | 0.0555 | |
python3.7 -m timeit 'uri="foo/bar/baz"' 'uri.startswith("/")' # 0.170 | 0.154 | |
python3.7 -m timeit 'uri="foo/bar/baz"' 'uri[0] == "/"' # 0.0468 | 0.0597 | |
# Array slicing from the beginning is much faster than str.startswith. | |
python -m timeit 'a=(1,2,3,4,5,6,7,8,9,0)' 'a[-5:][::-1]' # 0.146 | 0.192 | |
python -m timeit 'a=(1,2,3,4,5,6,7,8,9,0)' 'a[::-1][:5]' # 0.167 | 0.185 | |
python -m timeit 'a=(1,2,3,4,5,6,7,8,9,0)' 'a[:4:-1]' # 0.103 | 0.107 ! | |
# Performing a single slice is faster than slicing broken apart, but... | |
# reversing a smaller slice is faster than reversing a large one. (Duh. ;) | |
python -m timeit 'then = 951196249, now = 1551196249' '(now - then) > 0.5' # 0.117 | |
python -m timeit 'from datetime import datetime, timedelta; then = 951196249, now = 1551196249' \ | |
'(datetime.fromtimestamp(now) - datetime.fromtimestamp(then)) > timedelta(seconds=0.5)' # 2.4 µs |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment