@@ -68,6 +68,11 @@ def set_startup_timestamps(program_start=None, main_entry=None):
6868except ImportError :
6969 has_rl_utils = False
7070
71+ import csv
72+ from torch .autograd .profiler import DeviceType
73+ PROFILER_NCCL_FILTER = {"nccl" , "AllReduce" , "AllGather" , "AllToAll" , "Broadcast" , "ReduceScatter" }
74+ PROFILER_MEM_FILTER = {"memcpy" , "memset" }
75+
7176# Canonical list of RL timer names to include in timers_to_log.
7277# When the profiling branch is merged, this will be imported from rl_profiling
7378# as RL_LOGGABLE_TIMER_NAMES instead of being defined here.
@@ -3217,7 +3222,58 @@ def get_e2e_base_metrics():
32173222 def trace_handler (p ):
32183223 profile_dir = Path (f"{ args .tensorboard_dir } /../torch_profile" )
32193224 profile_dir .mkdir (parents = True , exist_ok = True )
3220- p .export_chrome_trace (f"{ profile_dir } /rank-{ torch .distributed .get_rank ()} .json.gz" )
3225+ rank = torch .distributed .get_rank ()
3226+ p .export_chrome_trace (f"{ profile_dir } /rank-{ rank } .json.gz" )
3227+ # CUDA kernel profiling
3228+ csv_cuda = f"{ profile_dir } /rank-{ rank } _cuda_kernel_non_comm.csv"
3229+ cuda_rows = []
3230+ for item in p .key_averages ():
3231+ op_name = item .key
3232+ # collect only non-communication ops with non-zero CUDA time
3233+ if hasattr (item , 'cuda_time_total' ) and item .cuda_time_total > 0 :
3234+ # filter out communication and memory copy ops
3235+ is_nccl = any (w .lower () in op_name .lower () for w in PROFILER_NCCL_FILTER )
3236+ is_mem = any (w .lower () in op_name .lower () for w in PROFILER_MEM_FILTER )
3237+ if is_nccl or is_mem :
3238+ continue
3239+ avg_cuda = item .cuda_time_total / item .count if item .count > 0 else 0.0
3240+ cuda_rows .append ({
3241+ "kernel_name" : op_name ,
3242+ "count" : item .count ,
3243+ "total_cuda_us" : item .cuda_time_total ,
3244+ "avg_cuda_us" : round (avg_cuda , 2 ),
3245+ "self_cuda_us" : item .self_cuda_time_total
3246+ })
3247+ cuda_rows .sort (key = lambda x : x ["total_cuda_us" ], reverse = True )
3248+ with open (csv_cuda , "w" , newline = "" , encoding = "utf-8-sig" ) as f :
3249+ field_names = ["kernel_name" , "count" , "total_cuda_us" , "avg_cuda_us" , "self_cuda_us" ]
3250+ writer = csv .DictWriter (f , fieldnames = field_names )
3251+ writer .writeheader ()
3252+ writer .writerows (cuda_rows )
3253+ print (f"[CUDA] non communication op list is saved to: { csv_cuda } " )
3254+ # PyTorch ATen op profiling
3255+ csv_torch = f"{ profile_dir } /rank-{ rank } _torch_aten_op.csv"
3256+ torch_rows = []
3257+ for item in p .key_averages ():
3258+ op_name = item .key
3259+ if item .device_type != DeviceType .CPU :
3260+ continue
3261+ avg_cpu = item .cpu_time_total / item .count if item .count > 0 else 0.0
3262+ torch_rows .append ({
3263+ "aten_op_name" : op_name ,
3264+ "count" : item .count ,
3265+ "total_cpu_us" : item .cpu_time_total ,
3266+ "avg_cpu_us" : round (avg_cpu , 2 ),
3267+ "self_cpu_us" : item .self_cpu_time_total
3268+ })
3269+
3270+ torch_rows .sort (key = lambda x : x ["total_cpu_us" ], reverse = True )
3271+ with open (csv_torch , "w" , newline = "" , encoding = "utf-8-sig" ) as f :
3272+ field_names = ["aten_op_name" , "count" , "total_cpu_us" , "avg_cpu_us" , "self_cpu_us" ]
3273+ writer = csv .DictWriter (f , fieldnames = field_names )
3274+ writer .writeheader ()
3275+ writer .writerows (torch_rows )
3276+ print (f"[Torch Aten] op list is saved to: { csv_torch } " )
32213277 prof = torch .profiler .profile (
32223278 schedule = torch .profiler .schedule (
32233279 wait = max (args .profile_step_start - 1 , 0 ),
0 commit comments