-- TODO: build some generic benchmark harness [case testBenchmarkTree] from typing import Optional class Node: def __init__(self, value: int) -> None: self.value = value self.left = None # type: Optional[Node] self.right = None # type: Optional[Node] def sum(self) -> int: left = 0 if self.left is not None: left = self.left.sum() right = 0 if self.right is not None: right = self.right.sum() return self.value + left + right def sum_tree(x: Optional[Node]) -> int: if x is None: return 0 return x.value + sum_tree(x.left) + sum_tree(x.right) def build(n: int) -> Optional[Node]: if n == 0: return None x = Node(n) x.left = build(n - 1) x.right = x.left return x def bench_sum(x: Optional[Node]) -> None: for i in range(1000000): sum_tree(x) def bench_sum_method(x: Node) -> None: for i in range(1000000): x.sum() [file driver.py] from typing import Optional import native import interpreted from timeit import timeit from time import time import os def dumb_time(f): t0 = time() f() t1 = time() return t1 - t0 def basic_test(m): tree = m.build(5) assert(m.sum_tree(tree) == 57) assert(tree.sum() == 57) return tree def test(m): tree = basic_test(m) g = {**globals(), **locals()} sum = timeit('m.sum_tree(tree)', globals=g) sum2 = timeit('tree.sum()', globals=g) fsum = dumb_time(lambda: m.bench_sum(tree)) fsum2 = dumb_time(lambda: m.bench_sum_method(tree)) build = timeit('m.build(5)', globals=g) return (sum, sum2, fsum, fsum2, build) # Basic functionality test basic_test(native) # Benchmark if we are benchmarking if os.environ.get('MYPYC_RUN_BENCH') == '1': nsum, nsum2, nfsum, nfsum2, nbuild = test(native) isum, isum2, ifsum, ifsum2, ibuild = test(interpreted) print(nsum, nsum2, nfsum, nbuild) print("Sum speedup:", isum/nsum) print("Sum method speedup:", isum2/nsum2) print("Sum (fast) speedup:", ifsum/nfsum) print("Sum (fast) method speedup:", ifsum2/nfsum2) print("Build speedup:", ibuild/nbuild) [case testBenchmarkVisitorTree] from mypy_extensions import trait from typing import cast, Generic, TypeVar, Any T = TypeVar('T') class Tree: def accept(self, v: 'TreeVisitor[T]') -> T: pass class Leaf(Tree): def accept(self, v: 'TreeVisitor[T]') -> T: return v.visit_leaf(self) class Node(Tree): def __init__(self, value: int, left: Tree, right: Tree) -> None: self.value = value self.left = left self.right = right def accept(self, v: 'TreeVisitor[T]') -> T: return v.visit_node(self) @trait class TreeVisitor(Generic[T]): def visit_leaf(self, x: Leaf) -> T: return cast(T, None) def visit_node(self, x: Node) -> T: return cast(T, None) class SumVisitor(TreeVisitor[int]): def sum(self, x: Tree) -> int: return x.accept(self) def visit_leaf(self, x: Leaf) -> int: return 0 def visit_node(self, x: Node) -> int: return x.value + self.sum(x.left) + self.sum(x.right) def equal(x: Tree, y: Tree) -> bool: return EqualVisitor(x).equal(y) class EqualVisitor(TreeVisitor[bool]): def __init__(self, left: Tree) -> None: self.left = left def equal(self, right: Tree) -> bool: return right.accept(self) def visit_leaf(self, right: Leaf) -> bool: return isinstance(self.left, Leaf) def visit_node(self, right: Node) -> bool: if isinstance(self.left, Node): # our boolean stuff is crap if (self.left.value == right.value and equal(self.left.left, right.left) and equal(self.left.right, right.right)): return True return False def sum_tree(x: Tree) -> int: return SumVisitor().sum(x) def build(n: int) -> Tree: if n == 0: return Leaf() return Node(n, build(n - 1), build(n - 1)) def bench_sum_tree(x: Tree) -> None: for i in range(100000): sum_tree(x) def bench_equal_tree(x: Tree, y: Tree) -> None: for i in range(100000): equal(x, y) [file driver.py] from typing import Optional import interpreted import native from timeit import timeit from time import time import os import sys # Side test: some stuff about MROs and generics if sys.version_info[:3] > (3, 5, 2): assert tuple(x.__name__ for x in interpreted.SumVisitor.mro()) == ('SumVisitor', 'TreeVisitor', 'Generic', 'object') assert tuple(x.__name__ for x in native.SumVisitor.mro()) == ('SumVisitor', 'TreeVisitor', 'Generic', 'object') assert str(native.TreeVisitor[native.T]) == "native.TreeVisitor[~T]" assert native.TreeVisitor.__name__ == "TreeVisitor" assert native.SumVisitor.__name__ == "SumVisitor" def dumb_time(f): t0 = time() f() t1 = time() return t1 - t0 def basic_test(m): tree = m.build(5) tree2 = m.build(5) tree2.right.right.right.value = 10 assert m.sum_tree(tree) == 57 assert m.equal(tree, tree) assert not m.equal(tree, tree2) assert isinstance(native.SumVisitor(), native.TreeVisitor) return tree def test(m): tree = basic_test(m) g = {**globals(), **locals()} fsum = dumb_time(lambda: m.bench_sum_tree(tree)) feq = dumb_time(lambda: m.bench_equal_tree(tree, tree)) return fsum, feq basic_test(native) if os.environ.get('MYPYC_RUN_BENCH') == '1': nfsum, nfeq = test(native) ifsum, ifeq = test(interpreted) print(nfsum) print("Sum speedup:", ifsum/nfsum) print("Equal speedup:", ifeq/nfeq)