m1n1/proxyclient/experiments/cpu_pstate_latencies.py

245 lines
5.8 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
import sys, pathlib, time
sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
from m1n1.setup import *
from m1n1 import asm
p.smp_start_secondaries()
tfreq = u.mrs(CNTFRQ_EL0)
TEST_CPUS = [1, 4]
CLUSTER_PSTATE = 0x20020
CLUSTER_STATUS = 0x20050
chip_id = u.adt["/chosen"].chip_id
if chip_id in (0x8103, 0x6000, 0x6001, 0x6002):
CREG = [
0x210e00000,
0x211e00000,
]
MAX_PSTATE = [5, 15]
elif chip_id in (0x8121, 0x6020, 0x6021, 0x6022):
CREG = [
0x210e00000,
0x211e00000,
]
if u.adt["/chosen"].target_type == "J416c":
MAX_PSTATE = [7, 19]
else:
MAX_PSTATE = [7, 17]
code = u.malloc(0x1000)
util = asm.ARMAsm(f"""
bench:
mrs x1, CNTPCT_EL0
1:
sub x0, x0, #1
cbnz x0, 1b
mrs x2, CNTPCT_EL0
sub x0, x2, x1
ret
signal_and_write:
sev
mrs x2, CNTPCT_EL0
add x2, x2, #0x800
1:
mrs x3, CNTPCT_EL0
sub x4, x3, x2
cbnz x4, 1b
str x1, [x0]
mov x0, x3
ret
timelog:
mrs x2, s3_1_c15_c0_0 /* SYS_IMP_APL_PMCR0 */
orr x2, x2, #1
msr s3_1_c15_c0_0, x2
mov x2, #0xffffffffffffffff
msr s3_1_c15_c1_0, x2
isb
wfe
1:
mrs x2, CNTPCT_EL0
mrs x3, s3_2_c15_c0_0
isb
stp x2, x3, [x0], #16
mov x4, #0x40
2:
sub x4, x4, #1
cbnz x4, 2b
sub x1, x1, #1
cbnz x1, 1b
ret
""", code)
iface.writemem(code, util.data)
p.dc_cvau(code, len(util.data))
p.ic_ivau(code, len(util.data))
def bench_cpu(idx, loops=10000000):
if idx == 0:
elapsed = p.call(util.bench, loops) / tfreq
else:
elapsed = p.smp_call_sync(idx, util.bench, loops) / tfreq
if elapsed == 0:
return 0
mhz = (loops / elapsed) / 1000000
return mhz
def set_pstate(cluster, pstate):
p.mask64(CREG[cluster] + CLUSTER_PSTATE, 0x1f01f, (1<<25) | pstate)
print()
LOG_ITERS = 10000
logbuf = u.malloc(LOG_ITERS * 16)
def bench_latency(cluster, cpu, from_pstate, to_pstate, verbose=False):
set_pstate(cluster, from_pstate)
bench_cpu(cpu)
p.smp_call(cpu, util.timelog, logbuf, LOG_ITERS)
psreg = (p.read64(CREG[cluster] + CLUSTER_PSTATE) & ~0x1f01f) | (1<<25) | to_pstate
tval = p.call(util.signal_and_write, CREG[cluster] + CLUSTER_PSTATE, psreg)
p.smp_wait(cpu)
logdata = iface.readmem(logbuf, LOG_ITERS * 16)
lts, lcyc = None, None
log = []
for i in range(LOG_ITERS):
ts, cyc = struct.unpack("<QQ", logdata [i*16:i*16+16])
log.append((ts, cyc))
off = 256
ts_0, cyc_0 = log[off]
ts_e, cyc_e = log[-1]
f_init = None
f_end = None
lts, lcyc = ts_0, cyc_0
inc = to_pstate > from_pstate
blip = 0
cnt = dts_sum = 0
for i in range(off, len(log)):
ts, cyc = log[i]
dts = ts - lts
dcyc = cyc - lcyc
cnt += 1
dts_sum += dts
blip = max(blip, dts)
if f_init is None and ts > tval:
tidx = i
f_init = (lcyc - cyc_0) / (lts - ts_0) * tfreq / 1000000
dts_init = dts_sum / cnt
if f_end is None and ts > (tval + ts_e) / 2:
f_end = (cyc_e - cyc) / (ts_e - ts) * tfreq / 1000000
cnt = dts_sum = 0
#if lts is not None:
#print(f"{i}: {ts}: {cyc} ({ts-lts}: {cyc-lcyc})")
#else:
#print(f"{i}: {ts}: {cyc}")
lts, lcyc = ts, cyc
dts_end = dts_sum / cnt
window = 32
if verbose:
print(f"Triggered at {tval}")
thresh = 2/ (1/f_init + 1/f_end)
for i in range(tidx, LOG_ITERS - window - 1):
ts0, cyc0 = log[i - window]
ts1, cyc1 = log[i + window]
f = (cyc1 - cyc0) / (ts1 - ts0) * tfreq / 1000000
if inc and (f > thresh) or ((not inc) and f < thresh):
tts = log[i][0]
tidx = i
if verbose:
print(f"Frequency transition at #{i} {tts}")
break
if verbose:
print(f"Initial frequency: {f_init:.2f}")
print(f"Final frequency: {f_end:.2f}")
print(f"Threshold: {thresh:.2f}")
for i in range(max(window, tidx - 10 * window), tidx + 10 * window):
ts0, cyc0 = log[i - window]
ts1, cyc1 = log[i + window]
lts, lcyc = log[i - 1]
ts, cyc = log[i]
f = (cyc1 - cyc0) / (ts1 - ts0) * tfreq / 1000000
print(f"{i}: {ts}: {cyc} ({ts-lts}: {cyc-lcyc}): {f:.2f}")
blip -= min(dts_init, dts_end)
return (tts - tval) / tfreq * 1000000000, blip / tfreq * 1000000000
for cluster, creg in enumerate(CREG):
cpu = TEST_CPUS[cluster]
freqs = []
print(f"#### Cluster {cluster} ####")
print(" P-States:")
print(" ", end="")
for pstate in range(MAX_PSTATE[cluster] + 1):
set_pstate(cluster, pstate)
freq = int(round(bench_cpu(cpu)))
freqs.append(freq)
print(f"{pstate}:{freq}MHz", end=" ")
print()
print()
print(" To-> |", end="")
for to_pstate in range(1, MAX_PSTATE[cluster] + 1):
print(f" {freqs[to_pstate]:7d} |", end="")
print()
print(" From |", end="")
for to_pstate in range(1, MAX_PSTATE[cluster] + 1):
print(f"---------+", end="")
print()
maxblip = 0
for from_pstate in range(1, MAX_PSTATE[cluster] + 1):
print(f" {freqs[from_pstate]:4d} |", end="")
for to_pstate in range(1, MAX_PSTATE[cluster] + 1):
if from_pstate == to_pstate:
print(f" ******* |", end="")
continue
lat, blip = bench_latency(cluster, cpu, from_pstate, to_pstate)
print(f" {lat:7.0f} |", end="")
maxblip = max(maxblip, blip)
print()
print()
print(f"Maximum execution latency spike: {maxblip:.0f} ns")
print()
print()
#bench_latency(1, TEST_CPUS[1], 15, 14, True)