运用PowerHint改进Android系统性能表现
背景介绍
公司需要改进既有的Android产品的整体用户操作体验,简单来说,就是在无法修改集成的第三方应用的前提下,想办法让用户操作系统的基本功能更加流程。
系统是Android 8.0,2xA73大核+2xA53小核,最高频率1.5GHz,内存和存储配置为2G DDR + 64G eMMC
可选方案
首先,主板本身的硬件配置无法变更,那么能修改的只有软件代码。
其次,跑在板子上的第三方应用我们无法修改。那么能够修改的只有系统层级的代码。
最后,要改善用户操作的体验,可以理解为让用户操作的响应更快。将目标分解,不外乎于用户打开应用速度更快、常用应用前后台切换不要重启、核心应用减少卡顿。
综上,我们的工作范围就可以圈定在:
- 感知用户打开应用的操作,并找到方法让后续应用开启过程耗时减少。
- 找到方法让更多的用户应用能在后台存活。
- 找到方法让核心应用使用时更为流畅。
凭借经验,一般可以类似下述手法实现上述需求(另一方面也可通过systrace分析具体卡顿点进行解决,不在本文讨论):
- 找到用户打开应用在系统中的必经之路,在应用开始启动前做类似cpu/gpu提频、后台无用进程清理、刷新率提升等动作;
- 在应用切到后台时做无UI进程清理释放内存、drop cache释放内存等动作;
- 在用户处于核心应用时判断用户操作进行进程绑定CPU大核、cpu提频、gpu提频等操作。
根据过往的经验,完成上述手法需要在framework中进行不少修改,包括在Activity启动路径打点、用户操作UserActivity打点、前后台切换打点等,是否有一种更加优美、更少侵入式修改的方案可以完成上述改动呢?
这就引出了本篇文章介绍的PowerHint接口。
PowerHint介绍
在电池类智能设备中,一个永恒的问题就是如何平衡功耗与性能,安卓的手机/平板等设备也不例外,安卓系统也针对这组矛盾做了非常多功课。
但是做为一个开放的生态系统,不同的芯片厂家、整机厂家总会有不同的调校偏好(比如有些设备是常供电设备,那么就可以无需考虑功耗问题而保障性能),所以安卓在系统中也给芯片/整机厂家进行客制化响应预留了软件架构,即PowerHAL,其接口位于Android\hardware\interfaces\power\1.0\之中。
先看下接口定义
package android.hardware.power@1.0;
/**
* Constructor for the interface performs power management setup actions at
* runtime startup, such as to set default cpufreq parameters.
*/
interface IPower {
/**
* setInteractive() performs power management actions upon the
* system entering interactive state (that is, the system is awake
* and ready for interaction, often with UI devices such as
* display and touchscreen enabled) or non-interactive state (the
* system appears asleep, display usually turned off). The
* non-interactive state may be entered after a period of
* inactivity in order to conserve battery power during
* such inactive periods.
*
* Typical actions are to turn on or off devices and adjust
* cpufreq parameters. This function may also call the
* appropriate interfaces to allow the kernel to suspend the
* system to low-power sleep state when entering non-interactive
* state, and to disallow low-power suspend when the system is in
* interactive state. When low-power suspend state is allowed, the
* kernel may suspend the system whenever no wakelocks are held.
*
* For example,
* This function can be called to enter non-interactive state after
* turning off the screen (if present) and called to enter
* interactive state prior to turning on the screen.
*
* @param interactive is true when the system is transitioning to an
* interactive state and false when transitioning to a
* non-interactive state.
*/
setInteractive(bool interactive);
/**
* powerHint() is called to pass hints on power requirements which
* may result in adjustment of power/performance parameters of the
* cpufreq governor and other controls.
*
* A particular platform may choose to ignore any hint.
*
* @param hint PowerHint which is passed
* @param data contains additional information about the hint
* and is described along with the comments for each of the hints.
*/
powerHint(PowerHint hint, int32_t data);
/**
* setFeature() is called to turn on or off a particular feature
* depending on the state parameter.
*
* @param feature Feature which needs to be set
* @param activate true/false to enable/disable the feature
*/
setFeature(Feature feature, bool activate);
/**
* Platform-level sleep state stats:
* Report cumulative info on the statistics on platform-level sleep states
* since boot.
*
* Higher the index in the returned <states> vector deeper the state is
* i.e. lesser steady-state power is consumed by the platform to be
* resident in that state.
*
* @return states of power states the device supports
* @return retval SUCCESS on success or FILESYSTEM_ERROR on filesystem
* nodes access error.
*/
getPlatformLowPowerStats()
generates (vec<PowerStatePlatformSleepState> states, Status retval);
};
可以看到,主要有两个接口,其一是setInteractive(),其在系统开屏/关屏时触发调用,用于让设备厂商去响应进行能耗调节;其二是powerHint(),其在系统的各种时机触发,也是用于让设备厂商获知当前系统状态,进行能耗调节。为了实现本文上述的目标,我们将重点放在powerHint接口,下面看下安卓定义了多少种Hint状态。
/** Power hint identifiers passed to powerHint() */
enum PowerHint : uint32_t {
/**
* Foreground app has started or stopped requesting a VSYNC pulse
* from SurfaceFlinger. If the app has started requesting VSYNC
* then CPU and GPU load is expected soon, and it may be appropriate
* to raise speeds of CPU, memory bus, etc. The data parameter is
* non-zero to indicate VSYNC pulse is now requested, or zero for
* VSYNC pulse no longer requested.
*/
VSYNC = 0x00000001,
/**
* User is interacting with the device, for example, touchscreen
* events are incoming. CPU and GPU load may be expected soon,
* and it may be appropriate to raise speeds of CPU, memory bus,
* etc. The data parameter is the estimated length of the interaction
* in milliseconds, or 0 if unknown.
*/
INTERACTION = 0x00000002,
/**
* DO NOT USE VIDEO_ENCODE/_DECODE! They will be removed in
* KLP.
*/
VIDEO_ENCODE = 0x00000003,
VIDEO_DECODE = 0x00000004,
/**
* Low power mode is activated or deactivated. Low power mode
* is intended to save battery at the cost of performance. The data
* parameter is non-zero when low power mode is activated, and zero
* when deactivated.
*/
LOW_POWER = 0x00000005,
/**
* Sustained Performance mode is actived or deactivated. Sustained
* performance mode is intended to provide a consistent level of
* performance for a prolonged amount of time. The data parameter is
* non-zero when sustained performance mode is activated, and zero
* when deactivated.
*/
SUSTAINED_PERFORMANCE = 0x00000006,
/**
* VR Mode is activated or deactivated. VR mode is intended to
* provide minimum guarantee for performance for the amount of time the
* device can sustain it. The data parameter is non-zero when the mode
* is activated and zero when deactivated.
*/
VR_MODE = 0x00000007,
/**
* This hint indicates that an application has been launched. Can be used
* for device specific optimizations during application launch. The data
* parameter is non-zero when the application starts to launch and zero when
* it has been launched.
*/
LAUNCH = 0x00000008,
};
可以看到,安卓系统已经做好了多种不同的事件定义等待制造商进行实现并响应。包括应用响应VSYNC信号、应用即将被启动、用户与设备进行交互,并且不同的事件响应是携带不同参数用于指定事件的起始/结束、持续时间等。
在注释中,安卓系统也明确写出,设备制造商应当实现这些事件的响应,快速调节CPU、GPU、ddr speed、io等多维度参数让设备整体硬件性能动态拉升到符合预期的状态,并在事件结束后将参数恢复。这样一来,相当于系统在需要执行一些重要任务时提前告知硬件提升频率(耗能提升),任务结束后告知硬件降低频率(耗能下降),如此就实现了能耗与性能的一种均衡。
那么这种策略与芯片自身的DVFS(动态电压频率调整)有何区别?我认为区别在于DVFS属于后调节,也就是说当前已经有了一个重任务在执行,DVFS才会逐步感知并提升频率,而上述PowerHint属于前调节,也就是系统知道马上要有一个重任务到来,告知硬件进行调频。显然,PowerHint这种方式能够更快速地让硬件进行响应,减少了使用DVFS调节逐步反应的时间,达到了更优体验。针对华为的Turbo技术比较可信的猜测就是结合了DVFS和AI驱动的前置调节,达到了其宣传中的体验。
可用范围
通过上述分析,针对前文的三个优化工作,可以逐个分析。
- 感知用户打开应用的操作,并找到方法让后续应用开启过程耗时减少。
- 找到方法让更多的用户应用能在后台存活。
- 找到方法让核心应用使用时更为流畅。
针对第一点,安卓在PowerManagerService的函数userActivity触发流程中,会调用powerHintInternal(PowerHint.INTERACTION, 0),对照上文的介绍,可以知道此时就是安卓系统告知硬件当下有用户操作,0的含义是由实现者自行决定硬件调频时间。那么我们实现PowerHAL中此函数,响应该case,对CPU/GPU进行锁最高频操作,并保持一定时间(如1s)后还原。如此一来,就实现了用户触屏点击某个应用图标后,应用启动之前 cpu/gpu已经拉满,从而让后续启动过程得到更快速响应,达到整个启动过程耗时减少的目的。
针对第二点,可以实现LAUNCH这个case,当任意具备页面的应用开始被启动时,触发一些 drop cache memory、清理无用进程的工作,减少启动至AMS流程后被动的oom adj触发可能性,也为应用预留更多内存空间。
针对第三点,可以结合LAUNCH和INTERACTION两个case做联合判断,进行后台清理和提频。当然这里有个问题在于如何识别当前应用为我们标记的核心应用?在PowerHint架构中是无法通过参数获取该信息的,可以写一个服务实现AccessibilityService组件,在其中可以监听系统Activity切换,从而将当前应用信息进行保存(比如存至property中),在PowerHAL实现中进行读取做结合判断。
总结
有时安卓系统本身已经为设备制造商做好了足够的预留,只是我们不一定能够准确找到并合理使用,才导致很多时间做了非常多侵入式修改,为自己日后做移植开发增加了额外工作量。通过合理实现原生架构,完全可以更简洁优美地达到目标。
当然上述通过PowerHAL架构做性能调优的一个重要前提是所用芯片具备这样的调节能力。如果用的芯片根本没有调频这种能力,那通过此架构来动态调频也无从谈起了。主芯片如此,外围的ddr、emmc/ufs 是否具备频率等级调整也是影响上述调节最终效果的重要因素。
在本文所用的板卡系统上,通过上述的方式,整体应用启动耗时、核心应用操作流程耗时客观数据有了20%左右的提升,代码模块化良好,无额外侵入,能够快速移植至其他同类芯片方案,效果符合预期。
本文介绍了如何借助Android的PowerHint接口提升系统性能,特别是在无法修改第三方应用的情况下。通过对用户操作、应用启动和核心应用运行的分析,提出通过PowerHint进行CPU/GPU调频、后台清理等操作,以提高应用启动速度、后台存活率和核心应用流畅性。这种方法相较于传统的DVFS具有前置调节的优势,能够在任务开始前调整硬件状态,提供更好的用户体验。实验证明,这种方法在特定设备上能有效提升20%左右的性能。

738

被折叠的 条评论
为什么被折叠?



