bpo-35884: Add variable access benchmarking script (GH-11725)
This commit is contained in:
parent
2c2ba05a6b
commit
f75d59e1a8
|
@ -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`.)
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Add a benchmark script for timing various ways to access variables:
|
||||
``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__))
|
Loading…
Reference in New Issue