Accelerate documentation
Profiler
Profiler
Profiler adalah alat yang memungkinkan pengumpulan metrik kinerja selama pelatihan dan inferensi. API context manager dari Profiler dapat digunakan untuk memahami lebih baik operator model mana yang paling mahal, memeriksa bentuk input dan stack trace-nya, mempelajari aktivitas kernel perangkat, serta memvisualisasikan jejak eksekusi. Alat ini memberikan wawasan tentang kinerja model Anda, sehingga Anda dapat mengoptimalkan dan memperbaikinya.
Panduan ini menjelaskan cara menggunakan PyTorch Profiler untuk mengukur waktu dan konsumsi memori dari operator model, serta cara mengintegrasikannya dengan Accelerate. Kami akan membahas berbagai kasus penggunaan dan menyediakan contoh untuk masing-masing.
Menggunakan profiler untuk menganalisis waktu eksekusi
Profiler memungkinkan Anda memeriksa operator mana yang dipanggil selama eksekusi rentang kode yang dibungkus dengan context manager profiler.
Mari kita lihat bagaimana kita dapat menggunakan profiler untuk menganalisis waktu eksekusi:
import torch
import torchvision.models as models
from torch.profiler import profile, record_function, ProfilerActivity
model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
with profile(activities=[ProfilerActivity.CPU], record_shapes=True) as prof:
model(inputs)
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))from accelerate import Accelerator, ProfileKwargs
import torch
import torchvision.models as models
model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
profile_kwargs = ProfileKwargs(
activities=["cpu"],
record_shapes=True
)
accelerator = Accelerator(cpu=True, kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
with accelerator.profile() as prof:
with torch.no_grad():
model(inputs)
print(prof.key_averages().table(sort_by="cpu_time_total", row_limit=10))Hasil tabel yang dihasilkan (beberapa kolom dihilangkan):
--------------------------------- ------------ ------------ ------------ ------------
Name Self CPU CPU total CPU time avg # of Calls
--------------------------------- ------------ ------------ ------------ ------------
aten::conv2d 171.000us 52.260ms 2.613ms 20
aten::convolution 227.000us 52.089ms 2.604ms 20
aten::_convolution 270.000us 51.862ms 2.593ms 20
aten::mkldnn_convolution 51.273ms 51.592ms 2.580ms 20
aten::batch_norm 118.000us 7.059ms 352.950us 20
aten::_batch_norm_impl_index 315.000us 6.941ms 347.050us 20
aten::native_batch_norm 6.305ms 6.599ms 329.950us 20
aten::max_pool2d 40.000us 4.008ms 4.008ms 1
aten::max_pool2d_with_indices 3.968ms 3.968ms 3.968ms 1
aten::add_ 780.000us 780.000us 27.857us 28
--------------------------------- ------------ ------------ ------------ ------------
Self CPU time total: 67.016msUntuk mendapatkan granularitas hasil yang lebih halus dan menyertakan bentuk input operator, berikan group_by_input_shape=True (catatan: ini memerlukan menjalankan profiler dengan record_shapes=True):
print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10))Menggunakan profiler untuk menganalisis konsumsi memori
Profiler juga dapat menampilkan jumlah memori (yang digunakan oleh tensor model) yang dialokasikan (atau dilepaskan) selama eksekusi operator model. Untuk mengaktifkan fungsi profiling memori, berikan profile_memory=True.
model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
with profile(activities=[ProfilerActivity.CPU],
profile_memory=True, record_shapes=True) as prof:
model(inputs)
print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10))model = models.resnet18()
inputs = torch.randn(5, 3, 224, 224)
profile_kwargs = ProfileKwargs(
activities=["cpu"],
profile_memory=True,
record_shapes=True
)
accelerator = Accelerator(cpu=True, kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
with accelerator.profile() as prof:
model(inputs)
print(prof.key_averages().table(sort_by="self_cpu_memory_usage", row_limit=10))Hasil tabel yang dihasilkan (beberapa kolom dihilangkan):
--------------------------------- ------------ ------------ ------------
Name CPU Mem Self CPU Mem # of Calls
--------------------------------- ------------ ------------ ------------
aten::empty 94.85 Mb 94.85 Mb 205
aten::max_pool2d_with_indices 11.48 Mb 11.48 Mb 1
aten::addmm 19.53 Kb 19.53 Kb 1
aten::mean 10.00 Kb 10.00 Kb 1
aten::empty_strided 492 b 492 b 5
aten::cat 240 b 240 b 6
aten::abs 480 b 240 b 4
aten::masked_select 120 b 112 b 1
aten::ne 61 b 53 b 3
aten::eq 30 b 30 b 1
--------------------------------- ------------ ------------ ------------
Self CPU time total: 69.332msJika Anda membutuhkan satu atau dua angka agregat untuk pelacakan eksperimen alih-alih tabel operator lengkap, gabungkan profiler dengan statistik memori runtime yang diungkap oleh PyTorch. Hal ini sering kali sudah cukup untuk mencatat penggunaan memori puncak per eksekusi:
import torch
from accelerate import Accelerator, PartialState, ProfileKwargs
# Resolve the device type/module (cuda, xpu, ...) instead of hardcoding CUDA
device_type = PartialState().device.type
device_module = getattr(torch, device_type)
def summarize_memory_stats():
return {
"peak_allocated_mb": device_module.max_memory_allocated() / 1024**2,
"peak_reserved_mb": device_module.max_memory_reserved() / 1024**2,
}
profile_kwargs = ProfileKwargs(
activities=["cpu", device_type],
profile_memory=True,
record_shapes=True,
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
device_module.reset_peak_memory_stats()
with accelerator.profile() as prof:
model(inputs)
print(prof.key_averages().table(sort_by=f"self_{device_type}_memory_usage", row_limit=10))
print(summarize_memory_stats())Pola ini menjaga keluaran profiler tetap tersedia untuk debugging sekaligus menghasilkan nilai ringkas yang dapat dicatat ke pelacak eksperimen.
Mengekspor jejak chrome
Anda dapat memeriksa urutan operator dan kernel CUDA yang diprofiling di Chrome trace viewer (chrome://tracing):
# Pick the available accelerator (cuda, xpu, ...) instead of hardcoding CUDA
device = torch.accelerator.current_accelerator()
device_activity = getattr(ProfilerActivity, device.type.upper())
model = models.resnet18().to(device)
inputs = torch.randn(5, 3, 224, 224).to(device)
with profile(activities=[ProfilerActivity.CPU, device_activity]) as prof:
model(inputs)
prof.export_chrome_trace("trace.json")model = models.resnet18()
profile_kwargs = ProfileKwargs(
activities=["cpu", PartialState().device.type],
output_trace_dir="trace"
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
inputs = torch.randn(5, 3, 224, 224).to(accelerator.device)
with accelerator.profile() as prof:
model(inputs)
# The trace will be saved to the specified directoryMenggunakan Profiler untuk Menganalisis Pekerjaan Berdurasi Panjang
Profiler menyediakan API tambahan untuk menangani tugas berdurasi panjang (seperti loop pelatihan). Melacak seluruh eksekusi dapat menjadi lambat dan menghasilkan file trace yang sangat besar. Untuk menghindari hal ini, gunakan argumen opsional:
schedule_option: Opsi penjadwalan memungkinkan Anda mengontrol kapan profiling aktif. Ini berguna untuk tugas yang berjalan lama agar tidak mengumpulkan terlalu banyak data. Kunci yang tersedia adalahwait,warmup,active,repeat, danskip_first. Profiler akan melewatiskip_firstlangkah pertama, kemudian menunggu selamawaitlangkah, kemudian melakukan pemanasan selamawarmuplangkah berikutnya, kemudian melakukan perekaman aktif selamaactivelangkah berikutnya, dan kemudian mengulang siklus mulai dariwaitlangkah. Jumlah siklus opsional ditentukan dengan parameterrepeat; nilai nol berarti siklus akan terus berlanjut hingga profiling selesai.on_trace_ready: menentukan fungsi yang menerima referensi ke profiler sebagai input dan dipanggil oleh profiler setiap kali trace baru siap.
Untuk mengilustrasikan cara kerja API, pertimbangkan contoh berikut:
from torch.profiler import schedule
my_schedule = schedule(
skip_first=1,
wait=5,
warmup=1,
active=3,
repeat=2
)
def trace_handler(p):
output = p.key_averages().table(sort_by=f"self_{device.type}_time_total", row_limit=10)
print(output)
p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json")
with profile(
activities=[ProfilerActivity.CPU, device_activity],
schedule=my_schedule,
on_trace_ready=trace_handler
) as p:
for idx in range(8):
model(inputs)
p.step()device_type = PartialState().device.type
def trace_handler(p):
output = p.key_averages().table(sort_by=f"self_{device_type}_time_total", row_limit=10)
print(output)
p.export_chrome_trace("/tmp/trace_" + str(p.step_num) + ".json")
profile_kwargs = ProfileKwargs(
activities=["cpu", device_type],
schedule_option={"wait": 5, "warmup": 1, "active": 3, "repeat": 2, "skip_first": 1},
on_trace_ready=trace_handler
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
model = accelerator.prepare(model)
with accelerator.profile() as prof:
for idx in range(8):
model(inputs)
prof.step()FLOPS
Gunakan rumus untuk memperkirakan FLOPs (floating point operations) dari operator tertentu (perkalian matriks dan konvolusi 2D).
Untuk mengukur operasi bilangan pecahan mengambang (FLOPS):
with profile(
activities=[ProfilerActivity.CPU, device_activity],
with_flops=True
) as prof:
model(inputs)
print(prof.key_averages().table(sort_by="flops", row_limit=10))profile_kwargs = ProfileKwargs(
with_flops=True
)
accelerator = Accelerator(kwargs_handlers=[profile_kwargs])
with accelerator.profile() as prof:
model(inputs)
print(prof.key_averages().table(sort_by="flops", row_limit=10))Hasil tabel yang dihasilkan (beberapa kolom dihilangkan):
------------------------------------------------------- ------------ ------------ ------------
Name Self CPU Self CUDA Total FLOPs
------------------------------------------------------- ------------ ------------ ------------
aten::conv2d 197.000us 0.000us 18135613440.000
aten::addmm 103.000us 17.000us 5120000.000
aten::mul 29.000us 2.000us 30.000
aten::convolution 409.000us 0.000us --
aten::_convolution 253.000us 0.000us --
aten::cudnn_convolution 5.465ms 2.970ms --
cudaEventRecord 138.000us 0.000us --
cudaStreamIsCapturing 43.000us 0.000us --
cudaStreamGetPriority 40.000us 0.000us --
cudaDeviceGetStreamPriorityRange 10.000us 0.000us --
------------------------------------------------------- ------------ ------------ ------------
Self CPU time total: 21.938ms
Self CUDA time total: 4.165msKesimpulan dan Informasi Lanjutan
PyTorch Profiler adalah alat yang ampuh untuk menganalisis kinerja model Anda. Dengan mengintegrasikannya dengan Accelerate, Anda dapat dengan mudah memprofil model Anda dan mendapatkan wawasan tentang kinerjanya, yang membantu Anda mengoptimalkan dan memperbaikinya.
Untuk informasi yang lebih detail, lihat dokumentasi PyTorch Profiler.
Update on GitHub