Accelerate documentation

Профилировщик

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v1.15.0).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Профилировщик

Profiler — это инструмент, позволяющий собирать метрики производительности во время обучения и инференса. API контекстного менеджера Profiler можно использовать, чтобы лучше понять, какие операторы модели являются наиболее затратными, изучить формы их входных данных и трассы стека, проанализировать активность ядер на устройстве и визуализировать трассу выполнения. Он даёт представление о производительности вашей модели, что позволяет оптимизировать и улучшить её.

В этом руководстве объясняется, как использовать PyTorch Profiler для измерения времени и потребления памяти операторами модели, а также как интегрировать это с Accelerate. Мы рассмотрим различные сценарии использования и приведём примеры для каждого из них.

Использование профайлера для анализа времени выполнения

Profiler позволяет проверить, какие операторы были вызваны во время выполнения участка кода, обёрнутого контекстным менеджером профилировщика.

Рассмотрим, как можно использовать profiler для анализа времени выполнения:

PyTorch
Accelerate
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))

Результат вывода таблицы (некоторые столбцы опущены):

---------------------------------  ------------  ------------  ------------  ------------  
                             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.016ms

Чтобы получить более детальные результаты и включить формы входных данных операторов, передайте group_by_input_shape=True (примечание: для этого необходимо запускать профилировщик с record_shapes=True):

print(prof.key_averages(group_by_input_shape=True).table(sort_by="cpu_time_total", row_limit=10))

Использование профайлера для анализа потребления памяти

Профилировщик также может отображать объём памяти (используемой тензорами модели), который был выделен (или освобождён) во время выполнения операторов модели. Чтобы включить профилирование памяти, передайте profile_memory=True.

PyTorch
Accelerate
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))

Результат вывода таблицы (некоторые столбцы опущены):

---------------------------------  ------------  ------------  ------------  
                             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.332ms

Если для отслеживания экспериментов вам нужны один или два сводных показателя, а не полная таблица операторов, объедините профилировщик со статистикой памяти во время выполнения, предоставляемой PyTorch. Этого часто достаточно для записи пикового потребления памяти за каждый запуск:

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())

Этот подход сохраняет вывод профилировщика доступным для отладки, одновременно генерируя компактные значения, которые можно записывать в трекер экспериментов.

Экспорт трассировки Chrome

Вы можете изучить последовательность профилируемых операторов и CUDA-ядер в Chrome trace viewer (chrome://tracing):

profile_export

PyTorch
Accelerate
# 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")

Использование Profiler для анализа длительных задач

Профайлер предлагает дополнительный API для обработки длительных задач (таких как циклы обучения). Трассировка всего выполнения может быть медленной и приводить к созданию очень больших файлов трассировки. Чтобы избежать этого, используйте необязательные аргументы:

  • schedule_option: Параметры планирования позволяют управлять тем, когда профилирование активно. Это полезно для длительных задач, чтобы избежать сбора избыточного объёма данных. Доступные ключи: wait, warmup, active, repeat и skip_first. Профилировщик пропускает первые skip_first шагов, затем ожидает wait шагов, после чего выполняет разгон в течение следующих warmup шагов, затем ведёт активную запись в течение следующих active шагов и повторяет цикл, начиная с шагов wait. Необязательное количество циклов задаётся параметром repeat; значение ноль означает, что циклы будут продолжаться до завершения профилирования.
  • on_trace_ready: определяет функцию, которая принимает ссылку на профилировщик в качестве входного параметра и вызывается профилировщиком каждый раз, когда новый трассировочный файл готов.

Для иллюстрации работы API рассмотрим следующий пример:

PyTorch
Accelerate
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()

FLOPS

Используйте формулу для оценки количества FLOPs (операций с плавающей точкой) для конкретных операторов (умножение матриц и 2D-свёртка).

Для измерения операций с плавающей точкой (FLOPS):

PyTorch
Accelerate
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))

Результат вывода таблицы (некоторые столбцы опущены):

-------------------------------------------------------  ------------  ------------  ------------  
                                                   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.165ms

Заключение и дополнительная информация

PyTorch Profiler — это мощный инструмент для анализа производительности ваших моделей. Интегрировав его с Accelerate, вы сможете легко профилировать модели и получать информацию об их производительности, что поможет оптимизировать и улучшить их.

Для получения более подробной информации обратитесь к документации PyTorch Profiler.

Update on GitHub