Compile Profiling¶
PyPTO includes built-in compile profiling that records wall-clock timings at each stage of the compilation pipeline — from frontend parsing through compiler passes and code generation to on-device execution.
Quick Start¶
Option 1: Environment Variable¶
Unsetting this variable (or setting it to 0) releases the environment-created
profiler on the next lookup, so ordinary JIT calls can reuse cached artifacts
again. An explicit with CompileProfiler() context remains active until its
scope ends, independently of the environment variable.
Option 2: ir.compile() Parameter¶
output_dir = ir.compile(program, profiling=True)
# Results are written to output_dir/report/pipeline_profile.{txt,json}
Option 3: Context Manager¶
from pypto.compile_profiling import CompileProfiler
with CompileProfiler() as prof:
@pl.program
class MyProgram:
...
ir.compile(MyProgram, ...)
print(prof.summary())
prof.to_json("profile.json")
Option 4: RunConfig¶
from pypto import ir
from pypto.runtime import RunConfig
config = RunConfig(compile_profiling=True)
compiled = ir.compile(MyProgram, **config.compile_kwargs())
# The report lands in `<compiled.output_dir>/report/`.
Output¶
Human-Readable Summary (pipeline_profile.txt)¶
PyPTO Compile Profile
======================
Total: 2.847s
parse 0.023s ( 0.8%)
passes 1.204s (42.3%)
UnrollLoops 0.012s ( 0.4%)
ConvertToSSA 0.034s ( 1.2%)
...
AllocateMemoryAddr 0.156s ( 5.5%)
codegen 0.418s (14.7%)
kernel_codegen:my_kernel 0.312s (11.0%)
orchestration_codegen 0.106s ( 3.7%)
device_execution 1.202s (42.2%)
Structured JSON (pipeline_profile.json)¶
{
"total_seconds": 2.847,
"stages": [
{"name": "parse", "seconds": 0.023, "children": []},
{"name": "passes", "seconds": 1.204, "children": [
{"name": "UnrollLoops", "seconds": 0.012, "children": []},
{"name": "ConvertToSSA", "seconds": 0.034, "children": []}
]},
{"name": "codegen", "seconds": 0.418, "children": [
{"name": "kernel_codegen:my_kernel", "seconds": 0.312, "children": []},
{"name": "orchestration_codegen", "seconds": 0.106, "children": []}
]}
]
}
Stage Hierarchy¶
These are all the stages the profiler can record. A single run reports only those its entry point reaches:
| Stage | Description |
|---|---|
compile |
Full compilation (wraps ir.compile()) |
parse |
@pl.program decorator AST parsing |
passes |
Pass pipeline execution |
| Per-pass stages | Individual pass timings (e.g., UnrollLoops, AllocateMemoryAddr) |
codegen |
Code generation |
kernel_codegen:<name> |
Per-kernel PTO/ptoas codegen |
orchestration_codegen |
Orchestration C++ codegen |
golden_write |
Golden reference file generation |
device_execution |
On-device compilation and execution (Simpler) |
When using ir.compile() directly, only passes and codegen (with
sub-stages) are recorded.
Programmatic API¶
from pypto.compile_profiling import CompileProfiler, get_active_profiler
# Check if profiling is active (explicit or via env var)
prof = get_active_profiler()
# Use as context manager
with CompileProfiler() as prof:
# Record custom stages
with prof.stage("my_custom_stage"):
do_something()
# Access results
data = prof.to_dict() # dict
text = prof.summary() # human-readable string
json_str = prof.to_json() # JSON string
prof.to_json("out.json") # write to file
prof.write_report("dir/") # write both .txt and .json
Overhead¶
When profiling is not enabled (the default), the overhead is a single
CompileProfiler.current() null-check per stage boundary — effectively zero.
When profiling is enabled, each stage records two time.perf_counter()
calls (sub-microsecond on modern hardware).
Related¶
- Runtime DFX (
RunConfig.enable_chip_swimlane,enable_dump_args,enable_pmu,enable_dep_gen) drives Simpler's per-task diagnostic artefacts — swimlane records, tensor I/O dumps, AICore PMU CSVs, and simpler dep_gen edges. The four flags are independent, share<work_dir>/dfx_outputs/as their output root, and are orthogonal to compile profiling. See 03-runtime-dfx.md.