From 413e716f9fb48338829435c4f243133589eb9fe6 Mon Sep 17 00:00:00 2001 From: Jason Madden Date: Wed, 11 Mar 2020 12:19:19 -0500 Subject: Avoid use of a metaclass by implementeng __getattribute__. This is pretty, but it slows down all attribute access to interfaces. By up to 25%. I'm not sure that's acceptable for things like Interface.providedBy. +-------------------------------------------+------------+-------------------------------+ | Benchmark | 38-master3 | 38-faster3 | +===========================================+============+===============================+ | read __module__ | 41.1 ns | 44.3 ns: 1.08x slower (+8%) | +-------------------------------------------+------------+-------------------------------+ | read __name__ | 41.3 ns | 51.6 ns: 1.25x slower (+25%) | +-------------------------------------------+------------+-------------------------------+ | read __doc__ | 41.8 ns | 53.3 ns: 1.28x slower (+28%) | +-------------------------------------------+------------+-------------------------------+ | read providedBy | 56.7 ns | 71.6 ns: 1.26x slower (+26%) | +-------------------------------------------+------------+-------------------------------+ | query adapter (no registrations) | 3.85 ms | 2.95 ms: 1.31x faster (-23%) | +-------------------------------------------+------------+-------------------------------+ | query adapter (all trivial registrations) | 4.59 ms | 3.65 ms: 1.26x faster (-20%) | +-------------------------------------------+------------+-------------------------------+ | contains (empty dict) | 136 ns | 55.4 ns: 2.45x faster (-59%) | +-------------------------------------------+------------+-------------------------------+ | contains (populated dict) | 137 ns | 55.0 ns: 2.49x faster (-60%) | +-------------------------------------------+------------+-------------------------------+ | contains (populated list) | 40.2 us | 2.95 us: 13.62x faster (-93%) | +-------------------------------------------+------------+-------------------------------+ --- benchmarks/micro.py | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'benchmarks') diff --git a/benchmarks/micro.py b/benchmarks/micro.py index 9031261..779298c 100644 --- a/benchmarks/micro.py +++ b/benchmarks/micro.py @@ -27,7 +27,7 @@ providers = [ for implementer in implementers ] -INNER = 10 +INNER = 100 def bench_in(loops, o): t0 = pyperf.perf_counter() @@ -50,8 +50,56 @@ def bench_query_adapter(loops, components): components.queryAdapter(provider, iface) return pyperf.perf_counter() - t0 +def bench_getattr(loops, name, get=getattr): + t0 = pyperf.perf_counter() + for _ in range(loops): + for _ in range(INNER): + get(Interface, name) # 1 + get(Interface, name) # 2 + get(Interface, name) # 3 + get(Interface, name) # 4 + get(Interface, name) # 5 + get(Interface, name) # 6 + get(Interface, name) # 7 + get(Interface, name) # 8 + get(Interface, name) # 9 + get(Interface, name) # 10 + return pyperf.perf_counter() - t0 + runner = pyperf.Runner() +# TODO: Need benchmarks of adaptation, etc, using interface inheritance. +# TODO: Need benchmarks of sorting (e.g., putting in a BTree) +# TODO: Need those same benchmarks for implementedBy/Implements objects. + +runner.bench_time_func( + 'read __module__', # stored in C, accessed through __getattribute__ + bench_getattr, + '__module__', + inner_loops=INNER * 10 +) + +runner.bench_time_func( + 'read __name__', # stored in C, accessed through PyMemberDef + bench_getattr, + '__name__', + inner_loops=INNER * 10 +) + +runner.bench_time_func( + 'read __doc__', # stored in Python instance dictionary directly + bench_getattr, + '__doc__', + inner_loops=INNER * 10 +) + +runner.bench_time_func( + 'read providedBy', # from the class, wrapped into a method object. + bench_getattr, + 'providedBy', + inner_loops=INNER * 10 +) + runner.bench_time_func( 'query adapter (no registrations)', bench_query_adapter, -- cgit v1.2.1