Skip to content

Commit 8bd159b

Browse files
authored
Micro-optimize TypeTraverserVisitor (#19459)
Specialize for concrete sequence types (`list` and `tuple`) for faster iteration, since the traversal code is very hot. I used trace logging (#19457) to identify functions where `PyObject_GetIter` was called frequently. This is a part of a set of micro-optimizations that improve self check performance by ~5.5%.
1 parent a82948b commit 8bd159b

File tree

1 file changed

+19
-9
lines changed

1 file changed

+19
-9
lines changed

mypy/typetraverser.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def visit_param_spec(self, t: ParamSpecType, /) -> None:
6767
t.default.accept(self)
6868

6969
def visit_parameters(self, t: Parameters, /) -> None:
70-
self.traverse_types(t.arg_types)
70+
self.traverse_type_list(t.arg_types)
7171

7272
def visit_type_var_tuple(self, t: TypeVarTupleType, /) -> None:
7373
t.default.accept(self)
@@ -78,11 +78,11 @@ def visit_literal_type(self, t: LiteralType, /) -> None:
7878
# Composite types
7979

8080
def visit_instance(self, t: Instance, /) -> None:
81-
self.traverse_types(t.args)
81+
self.traverse_type_tuple(t.args)
8282

8383
def visit_callable_type(self, t: CallableType, /) -> None:
8484
# FIX generics
85-
self.traverse_types(t.arg_types)
85+
self.traverse_type_list(t.arg_types)
8686
t.ret_type.accept(self)
8787
t.fallback.accept(self)
8888

@@ -93,15 +93,15 @@ def visit_callable_type(self, t: CallableType, /) -> None:
9393
t.type_is.accept(self)
9494

9595
def visit_tuple_type(self, t: TupleType, /) -> None:
96-
self.traverse_types(t.items)
96+
self.traverse_type_list(t.items)
9797
t.partial_fallback.accept(self)
9898

9999
def visit_typeddict_type(self, t: TypedDictType, /) -> None:
100100
self.traverse_types(t.items.values())
101101
t.fallback.accept(self)
102102

103103
def visit_union_type(self, t: UnionType, /) -> None:
104-
self.traverse_types(t.items)
104+
self.traverse_type_list(t.items)
105105

106106
def visit_overloaded(self, t: Overloaded, /) -> None:
107107
self.traverse_types(t.items)
@@ -115,16 +115,16 @@ def visit_callable_argument(self, t: CallableArgument, /) -> None:
115115
t.typ.accept(self)
116116

117117
def visit_unbound_type(self, t: UnboundType, /) -> None:
118-
self.traverse_types(t.args)
118+
self.traverse_type_tuple(t.args)
119119

120120
def visit_type_list(self, t: TypeList, /) -> None:
121-
self.traverse_types(t.items)
121+
self.traverse_type_list(t.items)
122122

123123
def visit_ellipsis_type(self, t: EllipsisType, /) -> None:
124124
pass
125125

126126
def visit_placeholder_type(self, t: PlaceholderType, /) -> None:
127-
self.traverse_types(t.args)
127+
self.traverse_type_list(t.args)
128128

129129
def visit_partial_type(self, t: PartialType, /) -> None:
130130
pass
@@ -136,7 +136,7 @@ def visit_type_alias_type(self, t: TypeAliasType, /) -> None:
136136
# TODO: sometimes we want to traverse target as well
137137
# We need to find a way to indicate explicitly the intent,
138138
# maybe make this method abstract (like for TypeTranslator)?
139-
self.traverse_types(t.args)
139+
self.traverse_type_list(t.args)
140140

141141
def visit_unpack_type(self, t: UnpackType, /) -> None:
142142
t.type.accept(self)
@@ -146,3 +146,13 @@ def visit_unpack_type(self, t: UnpackType, /) -> None:
146146
def traverse_types(self, types: Iterable[Type], /) -> None:
147147
for typ in types:
148148
typ.accept(self)
149+
150+
def traverse_type_list(self, types: list[Type], /) -> None:
151+
# Micro-optimization: Specialized for lists
152+
for typ in types:
153+
typ.accept(self)
154+
155+
def traverse_type_tuple(self, types: tuple[Type, ...], /) -> None:
156+
# Micro-optimization: Specialized for tuples
157+
for typ in types:
158+
typ.accept(self)

0 commit comments

Comments
 (0)