Spaces:
Running on Zero
Running on Zero
| # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| from __future__ import annotations | |
| import os | |
| from typing import TYPE_CHECKING, Callable, Any, Dict, List | |
| import pandas as pd | |
| import psutil | |
| import torch | |
| from lipforcing.callbacks.callback import Callback | |
| from lipforcing.utils.distributed import world_size, is_rank0, synchronize | |
| import lipforcing.utils.logging_utils as logger | |
| if TYPE_CHECKING: | |
| from lipforcing.methods import FastGenModel | |
| def log_prof_data(data_list: List[Dict[str, Any]]): | |
| # Create a table to log data with rank information | |
| metrics = list(data_list[0].keys()) | |
| # Initialize dictionaries to store min and max values for each metric | |
| min_values = {key: float("inf") for key in metrics} | |
| max_values = {key: float("-inf") for key in metrics} | |
| sum_values = {key: 0.0 for key in metrics} | |
| count = 0 | |
| for _rank, prof_data in enumerate(data_list): | |
| count += 1 | |
| # Update min, max, and sum values | |
| for key in metrics: | |
| min_values[key] = min(min_values[key], prof_data[key]) | |
| max_values[key] = max(max_values[key], prof_data[key]) | |
| sum_values[key] += prof_data[key] | |
| # Calculate average values | |
| avg_values = {key: sum_values[key] / count for key in metrics} | |
| summary_df = pd.DataFrame({"Avg": avg_values, "Max": max_values, "Min": min_values}) | |
| logger.info(f"GPU stats:\n{summary_df.to_string()}") | |
| class GPUStatsCallback(Callback): | |
| def __init__(self, every_n: int = 100): | |
| self.every_n = every_n | |
| def on_train_begin(self, model: FastGenModel, iteration: int = 0): | |
| torch.cuda.reset_peak_memory_stats() | |
| if hasattr(self, "config"): | |
| # overwritten by logging_iter if self.config exists | |
| self.every_n = self.config.trainer.logging_iter | |
| logger.info(f"every_n to measure gpus stats: {self.every_n}") | |
| def on_training_step_end( | |
| self, | |
| model: FastGenModel, | |
| data_batch: dict[str, torch.Tensor], | |
| output_batch: dict[str, torch.Tensor | Callable], | |
| loss_dict: dict[str, torch.Tensor], | |
| iteration: int = 0, | |
| ) -> None: | |
| del data_batch, output_batch, loss_dict | |
| if iteration % self.every_n == 0: | |
| cur_process = psutil.Process(os.getpid()) | |
| cpu_memory_usage = sum(p.memory_info().rss for p in [cur_process] + cur_process.children(recursive=True)) | |
| cpu_mem_gb = cpu_memory_usage / (1024**3) | |
| peak_gpu_mem_gb = torch.cuda.max_memory_allocated() / (1024**3) | |
| peak_gpu_mem_reserved_gb = torch.cuda.max_memory_reserved() / (1024**3) | |
| util = torch.cuda.utilization() | |
| prof_data = { | |
| "cpu_mem_gb": float(cpu_mem_gb), | |
| "peak_gpu_mem_gb": float(peak_gpu_mem_gb), | |
| "peak_gpu_mem_reserved_gb": float(peak_gpu_mem_reserved_gb), | |
| "util": float(util), | |
| } | |
| synchronize() | |
| data_list = [prof_data] * world_size() | |
| # this is blocking by default | |
| if world_size() > 1: | |
| torch.distributed.all_gather_object(data_list, prof_data) | |
| if is_rank0(): | |
| log_prof_data(data_list) | |
| synchronize() | |