← 全部文章

PC 端如何为鸿蒙测试建立高性能环境,并保持被测设备亮屏

测试前由 PC 端 HdcPerfController 通过 HDC 启动设备 SP_daemon,用 -lockfreq 建立高性能采集会话,并管理屏幕常亮:协议命令、保活与停止顺序、恢复记录,以及 SGU-AL10 上 16 份独立频率快照和两小时屏幕覆盖的实测。

我们把高性能运行条件放在 PC 端监控软件中管理。测试前,在 HdcPerfController 中选择“高性能 + 监控”,PC 端通过已授权的 HDC 连接启动设备的 SP_daemon,再通过内部采集协议提交高性能请求。测试应用继续负责自己的计算、线程调度和结果校验。

因此,单核测试曲线中的满频或接近满频平台,需要归到 PC 端建立的实验条件。应用内的 QoS、uclamp 和 affinity 用来表达线程需求与运行位置;它们的有效回读与设备的频率曲线分别保存。这样可以同时知道“负载在怎样的核心上执行”和“监控端为设备申请了怎样的性能状态”。

一次高性能采集会话按下面的顺序建立:

PC 端选择目标设备
→ 为会话生成随机 token
→ HDC 启动 SP_daemon -editorServer:<token>
→ 动态本地 TCP 端口转发到设备 tcp:8284
→ TCP init 命令携带 -lockfreq
→ start,接收采样,定期 keepalive
→ stop 确认,核对恢复并清理会话资源

HDC 负责跨设备连接,SP_daemon 负责设备内采集和性能请求。它们有各自的端口:手机的无线 HDC 端口、PC 的 HDC server 端口、设备上的 SmartPerf 8284,以及为当前会话动态分配的 PC 本地转发端口。代码始终绑定选定的 HDC target,避免把另一台手机当作同一次测试。

开始前先检查已有 daemon 和转发。SP_daemon 的 Editor 通道只接受一个客户端;发现 HiSmartPerf 或其他监控会话时,软件会报告冲突,由用户决定是否接管。启动后先核实带本次 token 的进程,再建立转发并使用真正的采集连接。一次性 TCP 探活连接可能占住这个通道,因此就绪检查不另外打开探活 socket。

会话入口的代码形态如下。hdc 是我们的进程调用封装,SmartPerfSession 是 TCP 协议封装;真正改变采集模式的是 lockFrequency: true:

var token = Convert
    .ToHexString(RandomNumberGenerator.GetBytes(16))
    .ToLowerInvariant();
var stopSignal = new TaskCompletionSource<bool>(
    TaskCreationOptions.RunContinuationsAsynchronously);

var daemonHost = hdc.StartLongRunningShell(
    target, $"SP_daemon -editorServer:{token}");

var forward = await hdc.CreateForwardAsync(target, localPort, 8284);
if (!forward.Success)
    throw new IOException($"HDC 转发失败:{forward.CombinedOutput}");

await using var session = new SmartPerfSession(token);
await session.ConnectAsync(localPort);
try
{
    await session.StartAsync(
        lockFrequency: true,
        intervalMs: 500,
        targetPackage: packageName,
        cancellationToken: runCancellation);

    // UI 停止操作调用 stopSignal.TrySetResult(true)。
    await stopSignal.Task.WaitAsync(runCancellation);
}
finally
{
    // 清理使用独立预算,不复用可能已经取消的 runCancellation。
    using var cleanup = new CancellationTokenSource(TimeSpan.FromSeconds(20));
    await session.StopAsync(cleanup.Token);
}

这段代码展示协议会话的启动与停止;daemon 就绪等待、失败启动后的恢复和 HDC 资源回收由外层 Service 负责。DisposeAsync() 只关闭本地连接和保活任务,不能代替显式 StopAsync()。start 已发出但响应丢失时,设备可能已经开始工作,所以 finally 不以“收到启动成功”为条件。stop 未确认时,外层保留恢复记录。

token 在启动 daemon 和后续控制命令中保持一致,每次会话重新生成。它与目标设备、daemon 身份和转发端口一同保存在会话状态中;恢复记录在启动前写入,屏幕原状态也在修改前写入。停止时据此确认资源归属。PC 保持启动 daemon 的 HDC 子进程与 TCP 会话生命周期,采集过程不依赖被测应用维持这条连接。

“仅监控”和“高性能 + 监控”使用同一条采样通道。高性能模式在 init 参数后追加 -lockfreq,完整命令形态是:

init::<token> -SESSIONID 1 -INTERVAL 500 -c -g -r -d -t -p -net -lockfreq

需要应用进程和帧率指标时,追加 -PKG <bundleName> -f。初始化、启动、保活和结束各有明确职责:

命令作用响应处理
init::<token> ... -lockfreq配置指标、间隔和高性能请求等待 init::True,识别失败响应
start::<token>启动当前采集任务等待 start::True,随后检查真实样本
keepalive::<token>保持当前 daemon 会话每 60 秒发送;该命令没有 ACK
stop::<token>停止采集和性能请求最长等待 12 秒的 stop::True,再核对清理

协议控制命令直接写入 UTF-8 字节,不自动追加换行。TCP 的接收边界不等于一条完整消息,所以控制响应与采样数据由持续读取器解析;发送端则串行写入,避免保活和停止命令穿插:

static async Task SendCommandAsync(
    NetworkStream stream,
    SemaphoreSlim writeGate,
    string command,
    CancellationToken cancellationToken)
{
    await writeGate.WaitAsync(cancellationToken);
    try
    {
        byte[] bytes = Encoding.UTF8.GetBytes(command);
        await stream.WriteAsync(bytes, cancellationToken);
    }
    finally
    {
        writeGate.Release();
    }
}

发送 init 或 start 前先注册相应响应等待器,避免响应很快返回时漏接。等待器同时识别 TOKEN_CHECK_FAILED、init::False、start::False 等失败文本。start::True 表示任务被接受,实际性能状态由接下来的逐核频率读回确认。

随文的 C# 协议辅助代码包含命令构造、参数检查、带超时的串行发送,以及屏幕状态解析和恢复命令选择。它可以作为接入起点;HDC 进程管理、响应等待器、采样解析和持久化恢复仍由完整会话层承担。

采样数据经过持续读取器合并 TCP 拆包、解析完整批次,再送往两个独立出口:原始 CSV 保存队列和界面显示队列。CSV 保留设备时间戳、接收顺序和逐核数值;界面只保留有限时间窗口,避免长时间监控不断占用内存。暂停曲线冻结的是显示视图,完整采样与保存继续进行。这个分工让用户可以查看某一段曲线,而不用中断正在运行的测试。

设备端接到 -lockfreq 后,进入其固件实现的性能管理路径。我们核对的设备二进制中,这条路径解析 PerfCmdHandle 并以约 4 秒周期维护请求。PC 端每 60 秒发送的 TCP keepalive 维持的是采集会话,设备端约 4 秒的周期维护的是性能请求,两者不能混作一个定时器。具体资源由固件解释,PC 的这条命令没有携带任意 MHz 目标参数。

在 SGU-AL10、系统 7.0.0.109 的一次频率验证中,高性能会话期间报告的 CPU 频点为:

核心组报告频率
CPU0–31.72 GHz
CPU4–112.27 GHz
CPU12–132.75 GHz

我们在开启前、会话保持期间和 stop 后分别做独立读回。会话期间 8/8 次快照中,14 个逻辑 CPU 均达到当轮报告的三个核心组上限;开启前为 0/3,恢复后为 0/5。SmartPerf 原始采样中另有 27/28 帧匹配该组频点。这里报告的是各次快照的命中数,不能据此把有限采样换算为整段时间的频点驻留率。

PC 端高性能会话的独立频率读回

SGU-AL10 · 7.0.0.109 · 14 个逻辑 CPU 均达到当轮报告上限的快照比例

050100命中快照比例 / %0 / 3 次开启前8 / 8 次高性能会话期间0 / 5 次stop 后

柱形表示有限采样的命中比例,不能换算为整段运行时间的频点驻留率。

数据表
阶段命中 / 快照数比例
开启前0 / 30%
高性能会话期间8 / 8100%
stop 后0 / 50%

下面把同一组独立快照按采集顺序展开,展示开启、保持和恢复时各核心组的实际读回。每个点是一份快照中的组内频率范围与均值;横轴是快照序号,不把两次读回之间的状态补成连续采样。

高性能会话的频率变化与恢复

SGU-AL10 · 7.0.0.109 · 各核心组报告频率的独立读回

开启前 · 3 次高性能会话期间 · 8 次stop 后 · 5 次CPU0–3 · GHz0123报告上限 1.72 GHzCPU4–11 · GHz0123报告上限 2.27 GHzCPU12–13 · GHz0123报告上限 2.75 GHz12345678910111213141516独立快照序号

点为组内均值,色带为组内最小至最大值,虚线是当轮报告上限。不跨阶段连线,不对快照间状态补样。

数据表
快照阶段相对时间 / sCPU0–3 均值(范围)/ GHzCPU4–11 均值(范围)/ GHzCPU12–13 均值(范围)/ GHz
1开启前0.01.530.619(0.558–0.85)1.2
2开启前3.21.530.582(0.558–0.75)1.2
3开启前6.41.530.661(0.558–1.38)1.2
4高性能会话期间13.31.722.272.75
5高性能会话期间16.41.722.272.75
6高性能会话期间19.51.722.272.75
7高性能会话期间22.61.722.272.75
8高性能会话期间25.81.722.272.75
9高性能会话期间28.91.722.272.75
10高性能会话期间31.91.722.272.75
11高性能会话期间35.01.722.272.75
12stop 后42.71.283(1.04–1.53)0.5581.2
13stop 后46.01.468(1.28–1.53)0.568(0.558–0.64)1.2
14stop 后49.31.408(1.04–1.53)0.5581.2
15stop 后52.61.438(1.16–1.53)0.5581.2
16stop 后55.91.378(0.92–1.53)0.5581.2

频率效果与固件有关。在 7.0.0.111 的另一次同机会话中,协议同样启动成功,但逐核频率仍在变化,软件显示“未命中金标”。因此界面分别报告协议状态与频率验证结果;-lockfreq 被接受并不保证每个固件、每个热状态都得到同一组固定频点。

停止时先取消并等待 keepalive 任务退出,再发送同一 token 的 stop。12 秒是 stop 响应的等待期限,整个清理还包括屏幕恢复、连接释放和资源复查。协议停止、屏幕设置或自有资源清理未确认时,会话恢复记录继续保留,重新连接同一设备后完成清理。直接结束 daemon 进程不能替代正常的停止协议。

频率恢复验证作为独立选项执行:确认 stop 并清理会话后,等待约 3 秒,读取 9 个逐核恢复样本。如果这个后置采样失败,界面报告“恢复未验证”;它不会把已经确认完成的协议清理改写成尚未停止,也不会重新创建已经清除的恢复记录。

屏幕常亮也属于监控会话管理。测试前勾选“监控时屏幕常亮”,PC 修改的是被测手机的临时自动熄屏覆盖值,控制动作经 HDC 发给手机的电源服务。

开启前先读取并保存基础超时与临时覆盖值:

hidumper -s PowerManagerService -a '-s'

随后优先请求无限临时超时:

power-shell timeout -o -1

有的商用固件明确拒绝 -1。只有当设备返回精确的 Override screen off time to -1 failed 时,程序才切换到两小时覆盖值:

power-shell timeout -o 7200000

7200000 的单位是毫秒。采用有限覆盖时,监控期间每 30 分钟重新提交一次;续期失败按一分钟间隔最多重试三次,并在界面显示异常。续期任务与数据接收分别运行,短暂的屏幕控制失败不会把已收到的采样丢掉。

设置超时不会自动点亮已经熄灭的屏幕,因此开始时再发送一次:

power-shell wakeup

程序随后重新读取电源服务,检查 OverrideTimeout 是否等于请求值,并确认 Current State: AWAKE。power-shell 即使打印失败,也可能以退出码 0 结束;因此判断依据是语义响应和读回状态。续期只更新超时,不重复发送 wakeup,从而保留用户手动熄屏的选择。

停止时的顺序同样重要:先取消并等待在途续期真正结束,再恢复原设置。否则,一条迟到的两小时续期可能覆盖刚刚完成的恢复。恢复命令由开启前保存的状态决定:

if (renewal is not null)
    await renewal.DisposeAsync(); // 取消续期,并等待在途命令结束。

string restoreCommand = original.OverrideTimeoutMs is int previous
    ? $"power-shell timeout -o {previous}"
    : "power-shell timeout -r";

await ShellAsync(target, restoreCommand);
// 再次读取 PowerManagerService,确认基础值和覆盖值均匹配原状态。

临时覆盖不是一个栈。原来已有覆盖值时,需要写回那个值;原来没有覆盖值时,才使用 timeout -r。原状态在修改设备前写入持久化会话记录,所以 PC 强退、手机离线或恢复失败后,仍能连接原设备继续恢复。

常亮功能也有完整实测。在 SGU-AL10、7.0.0.109 上,原设置为 600000 ms,即 10 分钟,没有临时覆盖。设备明确拒绝无限值后,程序成功设置 7200000 ms,即 2 小时的临时覆盖并读回 AWAKE。该次会话实际完成了 5 次约 30 分钟续期,停止后读回原来的 10 分钟基础设置,临时覆盖消失。

实测环节设备读回
监控开始前基础超时 600000 ms,无临时覆盖
常亮开启后基础超时 600000 ms,临时覆盖 7200000 ms,AWAKE
5 次定时续期均确认临时覆盖仍为 7200000 ms
停止并恢复基础超时回读 600000 ms,无临时覆盖

这次会话只有开始时发送了一次 wakeup。第五次续期时读回的电源状态已经是 SLEEP,超时覆盖仍成功续期,说明用户主动熄屏后不会被续期任务反复唤醒。这验证的是超时管理、续期和恢复,并不表示屏幕连续亮了两个半小时。

三个周期由不同任务承担:设备内部约 4 秒维护性能请求,PC 每 60 秒维持采集会话,有限屏幕覆盖每 30 分钟续期。暂停曲线只冻结显示,后台采样、CSV 保存和这些会话任务继续运行。采样间隔虽然请求为 500 ms,SGU-AL10 返回的新样本实测约为 1 Hz;图表使用设备真实时间戳,不复制样本补成更高频率。

评论

欢迎讨论,不用登录。评论会直接显示;骂人和广告会被删掉。

评论

    加载中……