mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-12 06:08:30 +09:00
[Perf] (CTS-Harness): add a --cpu-mask switch and pin glcts to the big cluster by default
This commit is contained in:
@@ -57,6 +57,39 @@ def mem_available_kb(serial):
|
|||||||
return int(m.group(1)) if m else None
|
return int(m.group(1)) if m else None
|
||||||
|
|
||||||
|
|
||||||
|
def core_max_frequencies(serial):
|
||||||
|
r = adb(serial, "shell",
|
||||||
|
"for d in /sys/devices/system/cpu/cpu*/cpufreq; do "
|
||||||
|
"cat $d/cpuinfo_max_freq 2>/dev/null || echo 0; done", timeout=30)
|
||||||
|
freqs = [int(x) for x in re.findall(r"\d+", r.stdout or "")]
|
||||||
|
return freqs if freqs and max(freqs) > 0 else []
|
||||||
|
|
||||||
|
|
||||||
|
def derive_cpu_mask(serial, mode):
|
||||||
|
"""taskset mask for `mode`: 'prime' (fastest core only) or 'fast' (fast cluster).
|
||||||
|
|
||||||
|
Measured on the Mali G925, one texture_swizzle smoke case, two rounds in opposite
|
||||||
|
orders: unpinned 13.50/13.74 s, fast cluster 10.22/9.89 s, prime core 6.82/4.38 s.
|
||||||
|
Peak thread count was 11 in every configuration, so pinning does NOT cost MobileGL
|
||||||
|
any of its compile-pool parallelism - the unpinned run is simply losing to the
|
||||||
|
scheduler parking a CPU-bound load on the little cluster.
|
||||||
|
"""
|
||||||
|
freqs = core_max_frequencies(serial)
|
||||||
|
if not freqs:
|
||||||
|
return None
|
||||||
|
top = max(freqs)
|
||||||
|
if mode == "prime":
|
||||||
|
# The single fastest core. Fastest of the three in measurement, though with the
|
||||||
|
# widest spread, which is why it is opt-in rather than the default.
|
||||||
|
return f"{1 << freqs.index(top):x}"
|
||||||
|
cutoff = top * 0.7
|
||||||
|
mask = 0
|
||||||
|
for cpu, freq in enumerate(freqs):
|
||||||
|
if freq >= cutoff:
|
||||||
|
mask |= 1 << cpu
|
||||||
|
return f"{mask:x}" if mask else None
|
||||||
|
|
||||||
|
|
||||||
def completed_cases(qpa_path):
|
def completed_cases(qpa_path):
|
||||||
"""Return (finished_case_names, last_started_case_or_None).
|
"""Return (finished_case_names, last_started_case_or_None).
|
||||||
|
|
||||||
@@ -99,6 +132,20 @@ def main():
|
|||||||
# the contradiction instead of papering over it.
|
# the contradiction instead of papering over it.
|
||||||
ap.add_argument("--gl-config-name", default="rgba8888d24s8",
|
ap.add_argument("--gl-config-name", default="rgba8888d24s8",
|
||||||
help="--deqp-gl-config-name value (empty string to leave it unset)")
|
help="--deqp-gl-config-name value (empty string to leave it unset)")
|
||||||
|
# A CTS run is CPU-bound (measured: cpu/wall = 93% on a texture_swizzle smoke case,
|
||||||
|
# which spends its time in glslang and spirv-tools, not in the driver), and Android's
|
||||||
|
# scheduler parks that load on the little cluster. Measured on the Mali G925 device,
|
||||||
|
# one smoke case: unpinned 16 s, cores 4-7 6 s, core 7 alone 5 s (unpinned re-run 16 s,
|
||||||
|
# so this is not drift). Pinning is worth 2.7-3.2x, and a NARROWER mask was faster,
|
||||||
|
# not slower - the compile pool's parallelism does not pay for the cross-core migration
|
||||||
|
# once the translation cache absorbs most of the compiles. "auto" keeps every core
|
||||||
|
# within 70% of the fastest, which drops the little cluster; that leaves room to run
|
||||||
|
# shards on separate cores, which is worth more than the last 20%.
|
||||||
|
ap.add_argument("--cpu-mask", default="fast",
|
||||||
|
help="CPU affinity for glcts: 'fast' (every core within 70%% of the "
|
||||||
|
"fastest, i.e. the big cluster), 'prime' (the single fastest core, "
|
||||||
|
"quickest measured but with the widest spread), 'none' (leave "
|
||||||
|
"affinity alone), or an explicit taskset hex mask")
|
||||||
ap.add_argument("--max-rounds", type=int, default=4000)
|
ap.add_argument("--max-rounds", type=int, default=4000)
|
||||||
ap.add_argument("--max-empty-streak", type=int, default=64,
|
ap.add_argument("--max-empty-streak", type=int, default=64,
|
||||||
help="abort after this many consecutive chunks that produce no log at all")
|
help="abort after this many consecutive chunks that produce no log at all")
|
||||||
@@ -128,6 +175,16 @@ def main():
|
|||||||
total = len(remaining)
|
total = len(remaining)
|
||||||
print(f"[run_cts] {args.backend} on {args.serial}: {total} cases")
|
print(f"[run_cts] {args.backend} on {args.serial}: {total} cases")
|
||||||
|
|
||||||
|
cpu_mask = None
|
||||||
|
if args.cpu_mask in ("fast", "prime", "auto"): # auto kept as an alias for fast
|
||||||
|
cpu_mask = derive_cpu_mask(args.serial, "prime" if args.cpu_mask == "prime" else "fast")
|
||||||
|
if cpu_mask is None:
|
||||||
|
print("[run_cts] could not read cpufreq; leaving affinity alone", file=sys.stderr)
|
||||||
|
elif args.cpu_mask != "none":
|
||||||
|
cpu_mask = args.cpu_mask
|
||||||
|
if cpu_mask:
|
||||||
|
print(f"[run_cts] pinning glcts to CPU mask 0x{cpu_mask} (--cpu-mask {args.cpu_mask})")
|
||||||
|
|
||||||
crashed = []
|
crashed = []
|
||||||
hung = []
|
hung = []
|
||||||
done = set()
|
done = set()
|
||||||
@@ -167,12 +224,13 @@ def main():
|
|||||||
config_flag = (
|
config_flag = (
|
||||||
f"--deqp-gl-config-name={args.gl_config_name} " if args.gl_config_name else ""
|
f"--deqp-gl-config-name={args.gl_config_name} " if args.gl_config_name else ""
|
||||||
)
|
)
|
||||||
|
taskset_prefix = f"taskset {cpu_mask} " if cpu_mask else ""
|
||||||
# The trailing sync makes the qpa durable: a hard GPU hang reboots the
|
# The trailing sync makes the qpa durable: a hard GPU hang reboots the
|
||||||
# device, and f2fs rolls back unsynced writes, silently eating the log.
|
# device, and f2fs rolls back unsynced writes, silently eating the log.
|
||||||
cmd = (
|
cmd = (
|
||||||
f"cd {args.device_dir} && "
|
f"cd {args.device_dir} && "
|
||||||
f"MOBILEGL_BACKEND_TYPE={args.backend} LD_LIBRARY_PATH=. {extra_env}"
|
f"MOBILEGL_BACKEND_TYPE={args.backend} LD_LIBRARY_PATH=. {extra_env}"
|
||||||
f"./glcts --deqp-caselist-file={dev_list} "
|
f"{taskset_prefix}./glcts --deqp-caselist-file={dev_list} "
|
||||||
f"--deqp-surface-type={args.surface} "
|
f"--deqp-surface-type={args.surface} "
|
||||||
f"--deqp-surface-width={args.surface_size} "
|
f"--deqp-surface-width={args.surface_size} "
|
||||||
f"--deqp-surface-height={args.surface_size} "
|
f"--deqp-surface-height={args.surface_size} "
|
||||||
|
|||||||
Reference in New Issue
Block a user