Skip to content

Commit 9f928c8

Browse files
committed
added PyTorch Profiler
1 parent 79678b9 commit 9f928c8

File tree

1 file changed

+145
-0
lines changed

1 file changed

+145
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
# coding: utf-8
2+
from typing import Any, Callable, Union
3+
import os
4+
from ignite.engine import Engine, Events
5+
import ignite.distributed as idist
6+
import datetime
7+
8+
import torch
9+
10+
11+
class PyTorchProfiler:
12+
"""PyTorch Profiler for performance debugging.
13+
14+
The PyTorch profiler is a tool that collects both GPU hardware and PyTorch related
15+
information, correlates them, performs automatic detection of bottlenecks in the model,
16+
and generates recommendations on how to resolve these bottlenecks.
17+
18+
Examples:
19+
.. code-block:: python
20+
21+
from ignite.handlers import PyTorchProfiler
22+
23+
trainer = ...
24+
model = ...
25+
optimizer = ...
26+
27+
pt_profiler = PyTorchProfiler(on_trace_ready="tensorboard", output_path="logs/train")
28+
pt_profiler.attach(trainer)
29+
30+
# Get profiler results of time
31+
pt_profiler.print_results()
32+
33+
# Save profiler result to CSV file (requires pandas)
34+
pt_profiler.write_results()
35+
36+
Both these methods can also be used as the on_trace_ready function which gets called after trace is ready.
37+
38+
pt_profiler = PyTorchProfiler(on_trace_ready=profiler.write_to_file(10), output_path="logs/train")
39+
40+
.. versionadded:: 0.4.8
41+
"""
42+
43+
def __init__(
44+
self,
45+
cuda_activity: bool = False,
46+
on_trace_ready: Union[Callable[..., Any], str] = "tensorboard",
47+
record_shapes: bool = False,
48+
profile_memory: bool = False,
49+
with_stack: bool = False,
50+
with_flops: bool = False,
51+
with_modules: bool = False,
52+
output_path: str = None,
53+
wait: int = 2,
54+
warmup: int = 2,
55+
active: int = 6,
56+
repeat: int = 1,
57+
) -> None:
58+
59+
self.activities = [torch.profiler.ProfilerActivity.CPU]
60+
if cuda_activity and torch.cuda.is_available():
61+
self.activities.append(torch.profiler.ProfilerActivity.GPU)
62+
63+
self.output_path = output_path
64+
65+
self.schedule = torch.profiler.schedule(wait=wait, warmup=warmup, active=active, repeat=repeat)
66+
67+
self.trace_handler = (
68+
torch.profiler.tensorboard_trace_handler(self.output_path)
69+
if on_trace_ready == "tensorboard"
70+
else on_trace_ready
71+
)
72+
73+
self.record_shapes = record_shapes
74+
self.profile_memory = profile_memory
75+
self.with_stack = with_stack
76+
self.with_flops = with_flops
77+
self.with_modules = with_modules
78+
79+
self.SORT_KEYS = {
80+
"cpu_time",
81+
"cuda_time",
82+
"cpu_time_total",
83+
"cuda_time_total",
84+
"cpu_memory_usage",
85+
"cuda_memory_usage",
86+
"self_cpu_memory_usage",
87+
"self_cuda_memory_usage",
88+
"count",
89+
}
90+
91+
def _profiler_create(self):
92+
self._profiler = torch.profiler.profile(
93+
activities=self.activities,
94+
schedule=self.schedule,
95+
on_trace_ready=self.trace_handler,
96+
record_shapes=self.record_shapes,
97+
profile_memory=self.profile_memory,
98+
with_stack=self.with_stack,
99+
with_flops=self.with_flops,
100+
with_modules=self.with_modules,
101+
)
102+
self._profiler.__enter__()
103+
104+
def _exit_profiler(self):
105+
self._profiler.__exit__()
106+
107+
def _profiler_step(self):
108+
self.profiler.step()
109+
110+
def attach(self, engine: Engine,) -> None:
111+
"""Attach the profiler to the engine.
112+
113+
Args:
114+
engine: engine object.
115+
"""
116+
117+
engine._event_handlers[Events.EPOCH_STARTED].append((self._profiler_create, {}, {}))
118+
engine._event_handlers[Events.GET_BATCH_COMPLETED].append((self._profiler_step, {}, {}))
119+
engine._event_handlers[Events.EPOCH_COMPLETED].append((self._profile_create.__exit__(), {}, {}))
120+
121+
def get_results(self, n: int = -1, sort_key: str = "self_cuda_memory_usage", top_level_events_only=False):
122+
if sort_key not in self.SORT_KEYS:
123+
raise ValueError(
124+
f" The sort_key {sort_key} is not accepted. Please choose a sort key from {self.SORT_KEYS}"
125+
)
126+
127+
return self.profiler.key_averages().table(
128+
sort_by=sort_key, row_limit=n, top_level_events_only=top_level_events_only
129+
)
130+
131+
def write_results(self, n: int = -1, sort_key: str = "self_cuda_memory_usage", top_level_events_only=False):
132+
try:
133+
import pandas as pd
134+
except ImportError:
135+
raise RuntimeError("Need pandas to write results as files")
136+
137+
results_df = pd.DataFrame(self.get_results(n, sort_key, top_level_events_only))
138+
139+
now = datetime.now().strftime("%Y%m%d-%H%M%S")
140+
file_name = f"{idist.backend()}_{now}.csv"
141+
142+
results_df.to_csv(os.path.join(self.output_path, file_name), index=False)
143+
144+
def print_results(self, n: int = -1, sort_key: str = "self_cuda_memory_usage", top_level_events_only=False):
145+
print(self.get_results(n, sort_key, top_level_events_only))

0 commit comments

Comments
 (0)