Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-26 08:22:17

0001 # SPDX-PackageName = "traccc, a part of the ACTS project"
0002 # SPDX-FileCopyrightText: CERN
0003 # SPDX-License-Identifier: MPL-2.0
0004 
0005 import argparse
0006 import sys
0007 import csv
0008 import git
0009 import pathlib
0010 import shutil
0011 import logging
0012 import tempfile
0013 import subprocess
0014 import os
0015 import time
0016 
0017 from traccc_bench_tools import parse_profile, types, build, profile, benchmark
0018 
0019 ALL_COMPUTE_CAPABILITIES = [
0020     "50",
0021     "52",
0022     "60",
0023     "61",
0024     "70",
0025     "75",
0026     "80",
0027     "86",
0028     "89",
0029     "90",
0030     "100",
0031     "120",
0032 ]
0033 
0034 
0035 log = logging.getLogger("traccc_benchmark")
0036 
0037 
0038 def main():
0039     parser = argparse.ArgumentParser()
0040 
0041     parser.add_argument(
0042         "repo",
0043         type=pathlib.Path,
0044         help="the traccc build repository",
0045     )
0046 
0047     parser.add_argument(
0048         "db",
0049         type=pathlib.Path,
0050         help="the CSV database",
0051     )
0052 
0053     parser.add_argument(
0054         "data",
0055         type=pathlib.Path,
0056         help="the traccc dataset to use",
0057     )
0058 
0059     parser.add_argument(
0060         "-v",
0061         "--verbose",
0062         help="enable verbose output",
0063         action="store_true",
0064     )
0065 
0066     parser.add_argument(
0067         "-j",
0068         "--parallel",
0069         help="number of threads to use for compilation",
0070         default=1,
0071         type=int,
0072     )
0073 
0074     parser.add_argument(
0075         "-e",
0076         "--events",
0077         help="number of events to process per commit",
0078         type=int,
0079         default=10,
0080     )
0081 
0082     parser.add_argument(
0083         "--cc",
0084         help="Compute Capability of the modelled GPU",
0085         type=str,
0086         required=True,
0087         dest="cc",
0088     )
0089 
0090     parser.add_argument(
0091         "--num-sm",
0092         help="number of SMs in the modelled GPU",
0093         type=int,
0094         required=True,
0095         dest="num_sm",
0096     )
0097 
0098     parser.add_argument(
0099         "--num-threads-per-sm",
0100         help="number of thread slots per SM in the modelled GPU",
0101         type=int,
0102         required=True,
0103         dest="num_threads_per_sm",
0104     )
0105 
0106     parser.add_argument(
0107         "--ncu-wrapper",
0108         help="wrapper to use around the ncu command",
0109         type=str,
0110         dest="ncu_wrapper",
0111     )
0112 
0113     args = parser.parse_args()
0114 
0115     logging.basicConfig(
0116         level=logging.DEBUG if (args.verbose or False) else logging.INFO,
0117         format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
0118     )
0119 
0120     log.info(
0121         "Using GPU with %d SMs and %d thread slots per SM (%d thread slots total)",
0122         getattr(args, "num_sm"),
0123         getattr(args, "num_threads_per_sm"),
0124         getattr(args, "num_sm") * getattr(args, "num_threads_per_sm"),
0125     )
0126 
0127     repo = git.Repo(args.repo)
0128 
0129     log.info("Using git repository at %s", repo.git_dir)
0130 
0131     if repo.is_dirty():
0132         e = "Repository is dirty; please clean it before use!"
0133         log.fatal(e)
0134         raise RuntimeError(e)
0135 
0136     if "TRACCC_TEST_DATA_DIR" not in os.environ:
0137         e = 'Environment variable "TRACCC_TEST_DATA_DIR" is not set; aborting!'
0138         log.fatal(e)
0139         raise RuntimeError(e)
0140 
0141     for exec in ["ncu", "g++", "nvcc", "cmake"]:
0142         if shutil.which(exec) is None:
0143             e = 'Executable "%s" is not available; aborting' % exec
0144             log.fatal(e)
0145             raise RuntimeError(e)
0146 
0147     old_commit_hash = repo.head.object.hexsha
0148     log.info("Current commit hash is %s", old_commit_hash)
0149 
0150     if args.db.is_file():
0151         log.info('Database file "%s" already exists; creating a backup', args.db)
0152         shutil.copy(str(args.db), str(args.db) + ".bak")
0153         results = []
0154         with open(args.db, "r") as f:
0155             reader = csv.DictReader(f)
0156             for i in reader:
0157                 results.append(i)
0158         log.info("Database contained %d pre-existing results", len(results))
0159     else:
0160         log.info('Database file "%s" does not exist; starting from scratch', args.db)
0161         results = []
0162 
0163     known_ccs = set(x["cc"] for x in results)
0164 
0165     log.info(
0166         "Currently have pre-existing results for %d Compute Capabilities",
0167         len(known_ccs),
0168     )
0169 
0170     for progress_id, x in enumerate(ALL_COMPUTE_CAPABILITIES):
0171         log.info(
0172             "Considering Compute Capability %s.%s (%u out of %u)",
0173             x[:-1],
0174             x[-1],
0175             progress_id,
0176             len(ALL_COMPUTE_CAPABILITIES),
0177         )
0178 
0179         # Skip commits which we have already benchmarked
0180         if x in known_ccs:
0181             log.info(
0182                 "Compute capability %s.%s is already know; skipping", x[:-1], x[-1]
0183             )
0184             continue
0185 
0186         if int(x) > int(args.cc):
0187             log.info(
0188                 "Compute Capability %s.%s is newer than target %s.%s; skipping",
0189                 x[:-1],
0190                 x[-1],
0191                 args.cc[:-1],
0192                 args.cc[-1],
0193             )
0194             continue
0195 
0196         try:
0197             log.info("Running benchmark for Compute Capability %s.%s", x[:-1], x[-1])
0198 
0199             result_df = benchmark.run_benchmark(
0200                 source_dir=args.repo,
0201                 data_dir=args.data,
0202                 commit=repo.head.object,
0203                 gpu_spec=types.GpuSpec(
0204                     getattr(args, "num_sm"), getattr(args, "num_threads_per_sm")
0205                 ),
0206                 parallel=args.parallel,
0207                 events=args.events,
0208                 ncu_wrapper=getattr(args, "ncu_wrapper", None),
0209                 cc=x,
0210             )
0211 
0212             for y in result_df.iloc:
0213                 results.append(
0214                     {
0215                         "cc": str(x),
0216                         "kernel": y["Kernel Name"],
0217                         "throughput": y["ThroughputMean"],
0218                         "throughput_dev": y["ThroughputStd"],
0219                         "rec_throughput": y["RecThroughputMean"],
0220                         "rec_throughput_dev": y["RecThroughputStd"],
0221                     }
0222                 )
0223 
0224         except Exception as e:
0225             log.exception(e)
0226         except KeyboardInterrupt as e:
0227             log.info("Received keyboard interrupt; skipping to post-processing")
0228             break
0229 
0230     log.info("Gathered a total of %d results (incl. pre-existing)", len(results))
0231 
0232     log.info("Writing data to %s", args.db)
0233     with open(args.db, "w") as f:
0234         writer = csv.DictWriter(
0235             f,
0236             fieldnames=[
0237                 "cc",
0238                 "kernel",
0239                 "throughput",
0240                 "throughput_dev",
0241                 "rec_throughput",
0242                 "rec_throughput_dev",
0243             ],
0244         )
0245         writer.writeheader()
0246 
0247         for i in results:
0248             writer.writerow(i)
0249 
0250     log.info("Processing complete; goodbye!")
0251 
0252 
0253 if __name__ == "__main__":
0254     main()