mirror of
				https://github.com/python/cpython.git
				synced 2025-10-30 21:21:22 +00:00 
			
		
		
		
	bpo-35884: Add variable access benchmarking script (GH-11725)
This commit is contained in:
		
							parent
							
								
									2c2ba05a6b
								
							
						
					
					
						commit
						f75d59e1a8
					
				
					 3 changed files with 282 additions and 0 deletions
				
			
		|  | @ -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``. | ||||
							
								
								
									
										272
									
								
								Tools/scripts/var_access_benchmark.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								Tools/scripts/var_access_benchmark.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Raymond Hettinger
						Raymond Hettinger