Skip to content

Commit d38f462

Browse files
DCPerf: Adding support for lower granularity to topdown monitors (#190)
Summary: This diff introduces support for specifying a lower granularity interval in Perfutil performance data collection, which is necessary for DCPerf mini, where the execution time is less than 45 seconds Key changes include: Added an interval parameter with a default of 5 seconds to various Perfutil classes (BasePerfUtil, AMDPerfUtil, ARMPerfUtil, NVPerfUtil). Updated constructors and run methods to accept and use the interval parameter for controlling data collection frequency. Modified configuration dictionaries to include interval values for the "topdown" monitor. Enhanced subprocess command construction to pass the interval argument to the underlying perf collection scripts. Reviewed By: YifanYuan3 Differential Revision: D79523911
1 parent ece2f32 commit d38f462

3 files changed

Lines changed: 39 additions & 13 deletions

File tree

benchpress/plugins/hooks/perf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"perfstat": {"interval": 5, "additional_events": []},
4040
"netstat": {"interval": 5, "additional_counters": []},
4141
"memstat": {"interval": 5, "additional_counters": []},
42-
"topdown": {},
42+
"topdown": {"interval": 5},
4343
"power": {"interval": 1},
4444
}
4545

benchpress/plugins/hooks/perf_monitors/topdown.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,12 @@ def get_os_release():
141141

142142

143143
class IntelPerfSpect(Monitor):
144-
def __init__(self, job_uuid, mux_interval_msecs=125, perfspect_path=None):
145-
# PerfSpect 1.x does not support specifying interval
146-
super(IntelPerfSpect, self).__init__(1, "perfspect", job_uuid)
144+
def __init__(self, interval, job_uuid, mux_interval_msecs=125, perfspect_path=None):
145+
# NOTE: PerfSpect 1.x does not support configurable sampling intervals.
146+
# The 'interval' parameter is accepted here only for API compatibility
147+
# with the DEFAULT_OPTIONS in perf.py, but it is not actually used
148+
# by this implementation.
149+
super(IntelPerfSpect, self).__init__(interval, "perfspect", job_uuid)
147150
self.mux_interval_msecs = mux_interval_msecs
148151
if perfspect_path is None:
149152
self.perfspect_path = os.path.join(BP_BASEPATH, "perfspect")
@@ -216,14 +219,12 @@ def write_csv(self):
216219
class IntelPerfSpect3(Monitor):
217220
def __init__(
218221
self,
222+
interval,
219223
job_uuid,
220-
report_interval_secs=5,
221224
mux_interval_msecs=125,
222225
perfspect_path=None,
223226
):
224-
super(IntelPerfSpect3, self).__init__(
225-
report_interval_secs, "perfspect3", job_uuid
226-
)
227+
super(IntelPerfSpect3, self).__init__(interval, "perfspect3", job_uuid)
227228
self.mux_interval_msecs = mux_interval_msecs
228229
if perfspect_path is None:
229230
self.perfspect_path = os.path.join(BP_BASEPATH, "perfspect")
@@ -282,14 +283,15 @@ def write_csv(self):
282283
class BasePerfUtil(Monitor):
283284
def __init__(
284285
self,
286+
interval,
285287
job_uuid,
286288
name,
287289
perf_collect_script_name,
288290
perf_postproc_script_name,
289291
perfutils_path=None,
290292
perf_postproc_args=None,
291293
):
292-
super(BasePerfUtil, self).__init__(0, name, job_uuid)
294+
super(BasePerfUtil, self).__init__(interval, name, job_uuid)
293295
if perfutils_path is None:
294296
self.perfutils_path = os.path.join(BP_BASEPATH, "perfutils")
295297
else:
@@ -314,6 +316,8 @@ def run(self):
314316
logger.warning(f"{perf_collect_script} does not exist")
315317
return
316318
cmd = [perf_collect_script]
319+
if self.interval is not None:
320+
cmd.append(str(self.interval))
317321
self.proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, encoding="utf-8")
318322
super(BasePerfUtil, self).run()
319323

@@ -355,20 +359,22 @@ def write_csv(self):
355359

356360

357361
class AMDPerfUtil:
358-
def __init__(self, job_uuid, **kwargs):
362+
def __init__(self, interval, job_uuid, **kwargs):
359363
self.cpuinfo = get_cpuinfo()
360364
self.cpu_vendor = get_cpu_vendor(self.cpuinfo)
361365
if self.cpu_vendor != "amd":
362366
raise Exception("Not an AMD processor!")
363367
self.amd_gen = get_amd_zen_generation(self.cpuinfo)
364368
self.perfutil = BasePerfUtil(
369+
interval,
365370
job_uuid,
366371
"amd-perf-collector",
367372
perf_collect_script_name="collect_amd_perf_counters.sh",
368373
perf_postproc_script_name="generate_amd_perf_report.py",
369374
)
370375
if self.amd_gen == "zen4":
371376
self.perfutil_zen4 = BasePerfUtil(
377+
interval,
372378
job_uuid,
373379
"amd-zen4-perf-collector",
374380
perf_collect_script_name="collect_amd_zen4_perf_counters.sh",
@@ -377,6 +383,7 @@ def __init__(self, job_uuid, **kwargs):
377383
)
378384
elif self.amd_gen == "zen5":
379385
self.perfutil = BasePerfUtil(
386+
interval,
380387
job_uuid,
381388
"amd-zen5-perf-collector",
382389
perf_collect_script_name="collect_amd_zen5_perf_counters.sh",
@@ -385,6 +392,7 @@ def __init__(self, job_uuid, **kwargs):
385392
)
386393
elif self.amd_gen == "zen5es":
387394
self.perfutil = BasePerfUtil(
395+
interval,
388396
job_uuid,
389397
"amd-zen5-perf-collector",
390398
perf_collect_script_name="collect_amd_zen5_perf_counters.sh",
@@ -416,7 +424,7 @@ class ARMPerfUtil(Monitor):
416424
"https://git.gitlab.arm.com/telemetry-solution/telemetry-solution.git"
417425
)
418426

419-
def __init__(self, job_uuid, interval=5):
427+
def __init__(self, interval, job_uuid, **kwargs):
420428
super(ARMPerfUtil, self).__init__(interval, "arm-perf-collector", job_uuid)
421429
self.avail = self.install_if_not_available()
422430
if not self.avail:
@@ -502,17 +510,21 @@ def write_csv(self):
502510

503511

504512
class NVPerfUtil(BasePerfUtil):
505-
def __init__(self, job_uuid, **kwargs):
513+
def __init__(self, interval, job_uuid, **kwargs):
506514
super(NVPerfUtil, self).__init__(
515+
interval,
507516
job_uuid,
508517
"nv-perf-collector",
509518
perf_collect_script_name="collect_nvda_neoversev2_perf_counters.sh",
510519
perf_postproc_script_name="generate_arm_perf_report.py",
511520
)
512521

522+
def run(self):
523+
super(NVPerfUtil, self).run()
524+
513525

514526
class DummyPerfUtil:
515-
def __init__(self, job_uuid, **kwargs):
527+
def __init__(self, interval, job_uuid, **kwargs):
516528
pass
517529

518530
def run(self):
@@ -538,6 +550,12 @@ def choose_perfspect():
538550
elif os.path.exists(perfspect1_bin1) and os.path.exists(perfspect1_bin2):
539551
return IntelPerfSpect
540552
else:
553+
logger.warning(f"Neither perfspect 1.x nor perfspect 3.x is available.\n \
554+
None of the followings exist:\n \
555+
{perfspect3_bin}\n \
556+
{perfspect1_bin1} \n \
557+
{perfspect1_bin2} \n \
558+
.")
541559
return DummyPerfUtil
542560

543561

perfutils/generate_amd_perf_report.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ def zen4_frontend_bound(grouped_df):
649649
return {"name": "Zen4 frontend Bound %", "series": frontend_bound_pct_series}
650650

651651

652+
@skip_if_missing
652653
def zen4_backend_bound(grouped_df):
653654
de_no_dispatch_per_slot_backend_stalls_series = grouped_df.get_group(
654655
"de_no_dispatch_per_slot.backend_stalls"
@@ -2580,6 +2581,13 @@ def main(
25802581
metrics.append(zen4_backend_bound(grouped_df))
25812582

25822583
filtered_metrics = list(itertools.filterfalse(lambda x: x is None, metrics))
2584+
2585+
if not filtered_metrics:
2586+
click.echo(
2587+
"No metrics could be calculated. All required counters are missing from the input data."
2588+
)
2589+
return
2590+
25832591
shortest_series = max(filtered_metrics, key=lambda m: m["series"].size)
25842592
df_metrics = concat_series(filtered_metrics, shortest_series)
25852593
if series:

0 commit comments

Comments
 (0)