Skip to content

Commit b3d5021

Browse files
authored
[mypyc] Include more operations in the trace log (#19647)
Add these operations to the trace log (note that trace logging is disabled by default): * Native attribute get/set * Boxing and unboxing * Casts * Incref/decref (including some implicit increfs) All of these are common operations and can be performance bottlenecks. There are ways to avoid or speed up most of these (possibly through new mypyc features). For example, incref/decref can sometimes be avoided by using borrowing.
1 parent a0c5238 commit b3d5021

File tree

1 file changed

+82
-7
lines changed

1 file changed

+82
-7
lines changed

mypyc/transform/log_trace.py

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,27 @@
99

1010
from __future__ import annotations
1111

12+
from typing import Final
13+
1214
from mypyc.ir.func_ir import FuncIR
13-
from mypyc.ir.ops import Call, CallC, CString, LoadLiteral, LoadStatic, Op, PrimitiveOp, Value
15+
from mypyc.ir.ops import (
16+
Box,
17+
Call,
18+
CallC,
19+
Cast,
20+
CString,
21+
DecRef,
22+
GetAttr,
23+
IncRef,
24+
LoadLiteral,
25+
LoadStatic,
26+
Op,
27+
PrimitiveOp,
28+
SetAttr,
29+
Unbox,
30+
Value,
31+
)
32+
from mypyc.ir.rtypes import none_rprimitive
1433
from mypyc.irbuild.ll_builder import LowLevelIRBuilder
1534
from mypyc.options import CompilerOptions
1635
from mypyc.primitives.misc_ops import log_trace_event
@@ -38,6 +57,18 @@ def get_load_global_name(op: CallC) -> str | None:
3857
return None
3958

4059

60+
# These primitives perform an implicit IncRef for the return value. Only some of the most common ones
61+
# are included, and mostly ops that could be switched to use borrowing in some contexts.
62+
primitives_that_inc_ref: Final = {
63+
"list_get_item_unsafe",
64+
"CPyList_GetItemShort",
65+
"CPyDict_GetWithNone",
66+
"CPyList_GetItem",
67+
"CPyDict_GetItem",
68+
"CPyList_PopLast",
69+
}
70+
71+
4172
class LogTraceEventTransform(IRTransform):
4273
def __init__(self, builder: LowLevelIRBuilder, fullname: str) -> None:
4374
super().__init__(builder)
@@ -48,7 +79,10 @@ def visit_call(self, op: Call) -> Value:
4879
return self.log(op, "call", op.fn.fullname)
4980

5081
def visit_primitive_op(self, op: PrimitiveOp) -> Value:
51-
return self.log(op, "primitive_op", op.desc.name)
82+
value = self.log(op, "primitive_op", op.desc.name)
83+
if op.desc.name in primitives_that_inc_ref:
84+
self.log_inc_ref(value)
85+
return value
5286

5387
def visit_call_c(self, op: CallC) -> Value:
5488
if global_name := get_load_global_name(op):
@@ -63,11 +97,53 @@ def visit_call_c(self, op: CallC) -> Value:
6397
elif func_name == "PyObject_VectorcallMethod" and isinstance(op.args[0], LoadLiteral):
6498
return self.log(op, "python_call_method", str(op.args[0].value))
6599

66-
return self.log(op, "call_c", func_name)
100+
value = self.log(op, "call_c", func_name)
101+
if func_name in primitives_that_inc_ref:
102+
self.log_inc_ref(value)
103+
return value
104+
105+
def visit_get_attr(self, op: GetAttr) -> Value:
106+
value = self.log(op, "get_attr", f"{op.class_type.name}.{op.attr}")
107+
if not op.is_borrowed and op.type.is_refcounted:
108+
self.log_inc_ref(op)
109+
return value
110+
111+
def visit_set_attr(self, op: SetAttr) -> Value:
112+
name = "set_attr" if not op.is_init else "set_attr_init"
113+
return self.log(op, name, f"{op.class_type.name}.{op.attr}")
114+
115+
def visit_box(self, op: Box) -> Value:
116+
if op.src.type is none_rprimitive:
117+
# Boxing 'None' is a very quick operation, so we don't log it.
118+
return self.add(op)
119+
else:
120+
return self.log(op, "box", str(op.src.type))
121+
122+
def visit_unbox(self, op: Unbox) -> Value:
123+
return self.log(op, "unbox", str(op.type))
124+
125+
def visit_cast(self, op: Cast) -> Value | None:
126+
value = self.log(op, "cast", str(op.type))
127+
if not op.is_borrowed:
128+
self.log_inc_ref(value)
129+
return value
130+
131+
def visit_inc_ref(self, op: IncRef) -> Value:
132+
return self.log(op, "inc_ref", str(op.src.type))
133+
134+
def visit_dec_ref(self, op: DecRef) -> Value:
135+
return self.log(op, "dec_ref", str(op.src.type))
136+
137+
def log_inc_ref(self, value: Value) -> None:
138+
self.log_event("inc_ref", str(value.type), value.line)
67139

68140
def log(self, op: Op, name: str, details: str) -> Value:
69-
if op.line >= 0:
70-
line_str = str(op.line)
141+
self.log_event(name, details, op.line)
142+
return self.add(op)
143+
144+
def log_event(self, name: str, details: str, line: int) -> None:
145+
if line >= 0:
146+
line_str = str(line)
71147
else:
72148
line_str = ""
73149
self.builder.primitive_op(
@@ -78,6 +154,5 @@ def log(self, op: Op, name: str, details: str) -> Value:
78154
CString(name.encode("utf-8")),
79155
CString(details.encode("utf-8")),
80156
],
81-
op.line,
157+
line,
82158
)
83-
return self.add(op)

0 commit comments

Comments
 (0)