[Python-checkins] bpo-35884: Add variable access benchmarking script (GH-11725)

Raymond Hettinger webhook-mailer at python.org
Sun Feb 3 01:54:59 EST 2019


https://github.com/python/cpython/commit/f75d59e1a896115bd52f543a417c665d6edc331f
commit: f75d59e1a896115bd52f543a417c665d6edc331f
branch: master
author: Raymond Hettinger <rhettinger at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2019-02-02T22:54:56-08:00
summary:

bpo-35884: Add variable access benchmarking script (GH-11725)

files:
A Misc/NEWS.d/next/Tools-Demos/2019-02-01-12-22-37.bpo-35884.hJkMRD.rst
A Tools/scripts/var_access_benchmark.py
M Doc/whatsnew/3.8.rst

diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst
index 09c43b1f30a5..a3982b0dfe09 100644
--- a/Doc/whatsnew/3.8.rst
+++ b/Doc/whatsnew/3.8.rst
@@ -551,3 +551,11 @@ CPython bytecode changes
 * Added new opcode :opcode:`END_ASYNC_FOR` for handling exceptions raised
   when awaiting a next item in an :keyword:`async for` loop.
   (Contributed by Serhiy Storchaka in :issue:`33041`.)
+
+
+Demos and Tools
+---------------
+
+* Added a benchmark script for timing various ways to access variables:
+  ``Tools/scripts/var_access_benchmark.py``.
+  (Contributed by Raymond Hettinger in :issue:`35884`.)
diff --git a/Misc/NEWS.d/next/Tools-Demos/2019-02-01-12-22-37.bpo-35884.hJkMRD.rst b/Misc/NEWS.d/next/Tools-Demos/2019-02-01-12-22-37.bpo-35884.hJkMRD.rst
new file mode 100644
index 000000000000..416eeac1d935
--- /dev/null
+++ b/Misc/NEWS.d/next/Tools-Demos/2019-02-01-12-22-37.bpo-35884.hJkMRD.rst
@@ -0,0 +1,2 @@
+Add a benchmark script for timing various ways to access variables:
+``Tools/scripts/var_access_benchmark.py``.
diff --git a/Tools/scripts/var_access_benchmark.py b/Tools/scripts/var_access_benchmark.py
new file mode 100644
index 000000000000..b4f3b9727056
--- /dev/null
+++ b/Tools/scripts/var_access_benchmark.py
@@ -0,0 +1,272 @@
+'Show relative speeds of local, nonlocal, global, and built-in access.'
+
+# Please leave this code so that it runs under older versions of
+# Python 3 (no f-strings).  That will allow benchmarking for
+# cross-version comparisons.  To run the benchmark on Python 2,
+# comment-out the nonlocal reads and writes.
+
+from collections import deque, namedtuple
+
+trials = [None] * 500
+steps_per_trial = 25
+
+class A(object):
+    def m(self):
+        pass
+
+class B(object):
+    __slots__ = 'x'
+    def __init__(self, x):
+        self.x = x
+
+class C(object):
+    def __init__(self, x):
+        self.x = x
+
+def read_local(trials=trials):
+    v_local = 1
+    for t in trials:
+        v_local;    v_local;    v_local;    v_local;    v_local
+        v_local;    v_local;    v_local;    v_local;    v_local
+        v_local;    v_local;    v_local;    v_local;    v_local
+        v_local;    v_local;    v_local;    v_local;    v_local
+        v_local;    v_local;    v_local;    v_local;    v_local
+
+def make_nonlocal_reader():
+    v_nonlocal = 1
+    def inner(trials=trials):
+        for t in trials:
+            v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal
+            v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal
+            v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal
+            v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal
+            v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal; v_nonlocal
+    inner.__name__ = 'read_nonlocal'
+    return inner
+
+read_nonlocal = make_nonlocal_reader()
+
+v_global = 1
+def read_global(trials=trials):
+    for t in trials:
+        v_global; v_global; v_global; v_global; v_global
+        v_global; v_global; v_global; v_global; v_global
+        v_global; v_global; v_global; v_global; v_global
+        v_global; v_global; v_global; v_global; v_global
+        v_global; v_global; v_global; v_global; v_global
+
+def read_builtin(trials=trials):
+    for t in trials:
+        oct; oct; oct; oct; oct
+        oct; oct; oct; oct; oct
+        oct; oct; oct; oct; oct
+        oct; oct; oct; oct; oct
+        oct; oct; oct; oct; oct
+
+def read_classvar_from_class(trials=trials, A=A):
+    A.x = 1
+    for t in trials:
+        A.x;    A.x;    A.x;    A.x;    A.x
+        A.x;    A.x;    A.x;    A.x;    A.x
+        A.x;    A.x;    A.x;    A.x;    A.x
+        A.x;    A.x;    A.x;    A.x;    A.x
+        A.x;    A.x;    A.x;    A.x;    A.x
+
+def read_classvar_from_instance(trials=trials, A=A):
+    A.x = 1
+    a = A()
+    for t in trials:
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+
+def read_instancevar(trials=trials, a=C(1)):
+    for t in trials:
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+
+def read_instancevar_slots(trials=trials, a=B(1)):
+    for t in trials:
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+
+def read_namedtuple(trials=trials, D=namedtuple('D', ['x'])):
+    a = D(1)
+    for t in trials:
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+        a.x;    a.x;    a.x;    a.x;    a.x
+
+def read_boundmethod(trials=trials, a=A()):
+    for t in trials:
+        a.m;    a.m;    a.m;    a.m;    a.m
+        a.m;    a.m;    a.m;    a.m;    a.m
+        a.m;    a.m;    a.m;    a.m;    a.m
+        a.m;    a.m;    a.m;    a.m;    a.m
+        a.m;    a.m;    a.m;    a.m;    a.m
+
+def write_local(trials=trials):
+    v_local = 1
+    for t in trials:
+        v_local = 1; v_local = 1; v_local = 1; v_local = 1; v_local = 1
+        v_local = 1; v_local = 1; v_local = 1; v_local = 1; v_local = 1
+        v_local = 1; v_local = 1; v_local = 1; v_local = 1; v_local = 1
+        v_local = 1; v_local = 1; v_local = 1; v_local = 1; v_local = 1
+        v_local = 1; v_local = 1; v_local = 1; v_local = 1; v_local = 1
+
+def make_nonlocal_writer():
+    v_nonlocal = 1
+    def inner(trials=trials):
+        nonlocal v_nonlocal
+        for t in trials:
+            v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1
+            v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1
+            v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1
+            v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1
+            v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1; v_nonlocal = 1
+    inner.__name__ = 'write_nonlocal'
+    return inner
+
+write_nonlocal = make_nonlocal_writer()
+
+def write_global(trials=trials):
+    global v_global
+    for t in trials:
+        v_global = 1; v_global = 1; v_global = 1; v_global = 1; v_global = 1
+        v_global = 1; v_global = 1; v_global = 1; v_global = 1; v_global = 1
+        v_global = 1; v_global = 1; v_global = 1; v_global = 1; v_global = 1
+        v_global = 1; v_global = 1; v_global = 1; v_global = 1; v_global = 1
+        v_global = 1; v_global = 1; v_global = 1; v_global = 1; v_global = 1
+
+def write_classvar(trials=trials, A=A):
+    for t in trials:
+        A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1
+        A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1
+        A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1
+        A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1
+        A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1;    A.x = 1
+
+def write_instancevar(trials=trials, a=C(1)):
+    for t in trials:
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+
+def write_instancevar_slots(trials=trials, a=B(1)):
+    for t in trials:
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+        a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1;    a.x = 1
+
+def read_list(trials=trials, a=[1]):
+    for t in trials:
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+
+def read_deque(trials=trials, a=deque([1])):
+    for t in trials:
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+
+def read_dict(trials=trials, a={0: 1}):
+    for t in trials:
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+        a[0];   a[0];   a[0];   a[0];   a[0]
+
+def list_append_pop(trials=trials, a=[1]):
+    ap, pop = a.append, a.pop
+    for t in trials:
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+
+def deque_append_pop(trials=trials, a=deque([1])):
+    ap, pop = a.append, a.pop
+    for t in trials:
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+        ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop(); ap(1); pop();
+
+def write_list(trials=trials, a=[1]):
+    for t in trials:
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+
+def write_deque(trials=trials, a=deque([1])):
+    for t in trials:
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+
+def write_dict(trials=trials, a={0: 1}):
+    for t in trials:
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+        a[0]=1; a[0]=1; a[0]=1; a[0]=1; a[0]=1
+
+def loop_overhead(trials=trials):
+    for t in trials:
+        pass
+
+
+if __name__=='__main__':
+
+    from timeit import Timer
+
+    for f in [
+            'Variable and attribute read access:',
+            read_local, read_nonlocal, read_global, read_builtin,
+            read_classvar_from_class, read_classvar_from_instance,
+            read_instancevar, read_instancevar_slots,
+            read_namedtuple, read_boundmethod,
+            '\nVariable and attribute write access:',
+            write_local, write_nonlocal, write_global,
+            write_classvar, write_instancevar, write_instancevar_slots,
+            '\nData structure read access:',
+            read_list, read_deque, read_dict,
+            '\nData structure write access:',
+            write_list, write_deque, write_dict,
+            '\nStack (or queue) operations:',
+            list_append_pop, deque_append_pop,
+            '\nTiming loop overhead:',
+            loop_overhead]:
+        if isinstance(f, str):
+            print(f)
+            continue
+        timing = min(Timer(f).repeat(7, 1000))
+        timing *= 1000000 / (len(trials) * steps_per_trial)
+        print(u'{:6.1f} \N{greek small letter mu}s\t{}'.format(timing, f.__name__))



More information about the Python-checkins mailing list