File indexing completed on 2026-07-26 08:22:17
0001
0002
0003
0004
0005 import pathlib
0006 import pandas
0007 import functools
0008 import numpy
0009 import tempfile
0010 import subprocess
0011 import operator
0012 import logging
0013
0014 from .types import GpuSpec
0015 from .kernels import simplify_name, map_name
0016 from .utils import harmonic_sum
0017
0018 log = logging.getLogger("traccc_bench_tools.parse_profile")
0019
0020
0021 def parse_triple(triple):
0022 assert triple[0] == "(" and triple[-1] == ")"
0023 x, y, z = triple[1:-1].split(", ")
0024 return int(x), int(y), int(z)
0025
0026
0027 def parse_profile_ncu(file: pathlib.Path, gpu_spec: GpuSpec, **kwargs):
0028 with tempfile.TemporaryDirectory() as tmpdirname:
0029 tmppath = pathlib.Path(tmpdirname)
0030
0031 profile_file = tmppath / "profile.csv"
0032
0033 with open(profile_file, "w") as f:
0034 subprocess.run(
0035 [
0036 "ncu",
0037 "-i",
0038 file,
0039 "--csv",
0040 "--print-units",
0041 "base",
0042 ],
0043 stdout=f,
0044 )
0045
0046 return parse_profile_csv(profile_file, gpu_spec, **kwargs)
0047
0048
0049 def parse_profile_csv(file: pathlib.Path, gpu_spec: GpuSpec, event_marker_kernel=None):
0050 if event_marker_kernel is None:
0051 event_marker_kernel = "ccl_kernel"
0052
0053 df = pandas.read_csv(file)
0054
0055 ndf = df[df["Metric Name"] == "gpu__time_duration.sum"][
0056 ["ID", "Kernel Name", "Block Size", "Grid Size", "Metric Value", "Metric Unit"]
0057 ]
0058
0059 assert (ndf["Metric Unit"] == "ns").all()
0060
0061 ndf["ThreadsPerBlock"] = df["Block Size"].apply(
0062 lambda x: functools.reduce(operator.mul, (parse_triple(x)))
0063 )
0064 ndf["BlocksPerGrid"] = df["Grid Size"].apply(
0065 lambda x: functools.reduce(operator.mul, (parse_triple(x)))
0066 )
0067 ndf["TotalThreads"] = ndf["ThreadsPerBlock"] * ndf["BlocksPerGrid"]
0068 ndf = ndf.drop(
0069 ["Block Size", "Grid Size", "ThreadsPerBlock", "BlocksPerGrid", "Metric Unit"],
0070 axis=1,
0071 )
0072 ndf["Metric Value"] = ndf["Metric Value"].apply(lambda x: int(x.replace(",", "")))
0073 ndf["Kernel Name"] = ndf["Kernel Name"].apply(simplify_name)
0074 ndf["Kernel Name"] = ndf["Kernel Name"].apply(map_name)
0075
0076 curr_evt_id = None
0077 evt_ids = []
0078 for x in ndf.iloc:
0079 if x["Kernel Name"] == event_marker_kernel:
0080 if curr_evt_id is None:
0081 curr_evt_id = 0
0082 else:
0083 curr_evt_id += 1
0084 evt_ids.append(curr_evt_id)
0085
0086 num_unidentified_event_ids = sum(1 for x in evt_ids if x is None)
0087
0088 if num_unidentified_event_ids > 0:
0089 log.warning(
0090 "Event ID could not be determined for %d kernels",
0091 num_unidentified_event_ids,
0092 )
0093
0094 ndf["EventID"] = evt_ids
0095
0096 thr_occ = df[df["Metric Name"] == "Theoretical Occupancy"]
0097
0098 ndf = ndf.merge(
0099 thr_occ[["ID", "Metric Value"]],
0100 on="ID",
0101 how="left",
0102 validate="one_to_one",
0103 suffixes=("", "R"),
0104 )
0105
0106 ndf["Occupancy"] = ndf["Metric ValueR"].apply(lambda x: float(x) / 100.0)
0107
0108 ndf["k"] = ndf["TotalThreads"] / (
0109 gpu_spec.n_sm * gpu_spec.n_threads_per_sm * ndf["Occupancy"]
0110 )
0111
0112 ndf["Latency"] = ndf["Metric Value"] / 1e9
0113
0114 ndf["Throughput"] = (numpy.ceil(ndf["k"]) / ndf["k"]) / (ndf["Metric Value"] / 1e9)
0115 ndf["RecThroughput"] = 1.0 / ndf["Throughput"]
0116
0117 ndf = ndf.drop(["Metric ValueR"], axis=1)
0118
0119 ndf = ndf.groupby(["Kernel Name", "EventID"], as_index=False).agg(
0120 {"Throughput": harmonic_sum, "RecThroughput": "sum", "Latency": "sum"},
0121 )
0122
0123 ndf = ndf.groupby("Kernel Name", as_index=False).agg(
0124 ThroughputMean=("Throughput", "mean"),
0125 ThroughputStd=("Throughput", "std"),
0126 RecThroughputMean=("RecThroughput", "mean"),
0127 RecThroughputStd=("RecThroughput", "std"),
0128 LatencyMean=("Latency", "mean"),
0129 LatencyStd=("Latency", "std"),
0130 )
0131
0132 return ndf.fillna(0)