Application thread scheduler delay
Scheduler delay:应用程序是可运行状态(Runable),但在相当长的时间内不会运行。Scheduler delay 可能会引起Jank。不同性质的线程对Scheduler delay 要求不同,
可以在systrace中通过线程正在运行的段前面的蓝色条来标识可运行时间。可运行时间还可以由sched_wakeup线程 sched_switch事件与发出线程执行开始信号的事件之间的时间长度来确定。
如果应用程序UI线程长时间在可运行状态,则可能会导致问题。有如下的方法可以解决长时间的Runable 状态:
Interrupt traffic
由于ARM平台默认情况下由CPU 0来处理中断,因此我们建议使用IRQ平衡器,来平衡IRQ 的处理。
在debug过程中,我们看到了造成问题的根源可能是CPU 0中断过多。例如,如果display 线程是在CPU 0上调度的,然而由于CPU0 处理了大量的中断,而导致display 长时间的Runable ,而display 需要在很短的时间内完成时间的工作,如果中断程序过多,那么display 线程将不会得到及时调度而产生Jank。最初,我们尝试通过将display 线程的CPU亲和力设置为CPU 1-3来解决此问题,以避免与中断争用,但是后来我们意识到尚未启用irqbalance。启用irqbalance后,由于减少了其他中断的竞争,即使display 和display 中断都在同一CPU上,jank也得到了明显改善。
可以通过查看systrace中sched和irq部分,来查找问题。sched部分显示了已调度的内容,但是irq部分中的重叠区域表示在该时间内正在运行中断,而不是正常调度的进程。如果您看到中断期间花费了大量时间,则可以选择以下选项:
通常不建议设置CPU亲和力,但在特定情况下可能有用。通常,很难预测大多数常见中断的系统状态,但是如果您有一组非常特殊的条件来触发某些中断,这些中断会导致系统比正常情况下更受限制(例如VR),则显式CPU亲和力可能是一个很好的解决方案。
Long softirqs
当softirq运行时,它将禁用抢占。softirqs也可以在内核中的许多地方触发,并且可以在用户进程内部运行。如果有足够的softirq活动,则用户进程将停止运行softirq,ksoftirqd将唤醒以运行softirq并进行负载平衡。通常,这很好。但是,一个很长的softirq可能会对系统造成严重破坏。
例如:Janks issue 打开Wi-Fi stream
我们看到VR性能不一致。我们在systrace中发现,单个NET_RX softirq可以运行30毫秒以上。最终,这被跟踪到了接收数据包导向功能,这是一种Wi-Fi功能,可将许多NET_RX软irq合并为一个软irq。在正确的条件下,生成的softirq可能具有非常大的(可能无限制的)运行时。
尽管此功能可能减少了用于网络的总CPU周期,但它阻止了系统在正确的时间运行正确的事情。禁用该功能不会影响网络吞吐量或电池寿命,但它确实通过允许ksoftirqd负载平衡softirq而不是规避它来解决了VR跟踪问题。
softirq在systrace的irq部分中可见,如果看到softirq运行时间过长,请检查softirq的实现。
Drivers leaving preemption or IRQs disabled too long
禁用抢占或中断时间过长(数十毫秒)会导致Jank。通常,此问题表现为一个线程变得可运行,但未在特定CPU上运行,即使该可运行线程的优先级(或SCHED_FIFO)明显高于其他线程。
Incorrect use of workqueues
中断处理程序通常需要执行可以在中断上下文之外运行的工作,从而使工作可以移植到内核中的不同线程。驱动程序开发人员可能会注意到内核具有非常方便的全系统异步任务功能,称为工作队列workqueues ,并且可以将其用于与中断有关的工作。
但是,工作队列可能会导致Jank,因为它们始终是SCHED_OTHER。许多硬件中断是性能的关键,必须立即运行。工作队列不能保证何时运行。每当我们看到关键性能路径上的工作队列时,无论使用哪种设备,都可能导致Jank。在配备旗舰处理器的上,我们看到,如果设备处于负载状态,则单个工作队列最多可能会延迟7毫秒,具体取决于调度程序行为和系统上运行的其他操作。
需要工作在单独线程中的类似中断的工作的驱动程序应该创建自己的SCHED_FIFO kthread,而不是工作队列。
Framework lock contention
Framework lock争用可能是造成问题或其他性能问题的根源。它通常是由ActivityManagerService锁引起的,但也可以在其他锁中看到。例如,PowerManagerService锁定会影响屏幕的性能。如果您在设备上看到此错误,则没有好的修复方法,因为只能通过对Framework 进行体系结构改进来加以改进。但是,如果您要修改在system_server内部运行的代码,那么避免长时间持有锁,尤其是ActivityManagerService锁,至关重要。
fd contention within a process
这是罕见的。
就是说,如果您在一个进程中有多个线程在编写相同的fd,则有可能看到该fd的争用。
Unnecessary CPU idle transitions
在处理IPC(尤其是多进程)时,通常会看到以下运行时行为的变化:
开销的常见来源介于步骤2和3之间。如果CPU 2处于空闲状态,则必须使其回到活动状态,然后线程B才能运行。根据SOC和空闲状态的深度,线程B开始运行之前,可能需要数十微秒。如果IPC每一侧的实际运行时间足够接近开销,则CPU空闲转换会大大降低该管道的整体性能。Android最常见的问题是与 binder transactions有关,许多使用 binder transactions的服务最终看起来像上述情况。
首先,wake_up_interruptible_sync()在内核驱动程序中使用该功能,该功能要调度器支持。 binder transactions现在使用此功能,它对同步 binder transactions很有帮助,避免了不必要的CPU空闲转换。
其次,确保您的cpuidle过渡时间是优化过的,并且cpuidle调速器已正确考虑了这些时间。如果您的SOC进入或退出最深的空闲状态,则无法通过进入最深的空闲状态来节省功耗。
Logging
不好的log 对性能影响较大,请删除所有调试日志。
I/O issues
I / O操作是Jank的常见来源。如果线程访问内存映射文件,并且页面不在页面高速缓存中,则它将发生故障并从磁盘读取页面。这会阻塞线程(通常持续10毫秒以上),并且如果它发生在UI渲染的关键路径中,则可能会导致jank。这里有太多的I / O操作原因需要讨论,但是在尝试改善I / O行为时请检查以下位置:
Page cache thrashing
有足够可用内存的设备在执行长时间运行的操作(例如打开新应用程序)时可能会突然变得非常缓慢。对该应用程序的跟踪可能表明它在特定运行期间始终被I / O阻止,即使该应用程序通常未在I / O中被阻止。这通常是页面缓存抖动的迹象,尤其是在内存较少的设备上。
识别此错误的一种方法是使用pagecache标记获取systrace,并将该跟踪输入到脚本 system/extras/pagecache/pagecache.py。pagecache.py将文件映射到页面缓存的单个请求转换为每个文件的汇总统计信息。如果发现文件中读取的字节数多于磁盘上该文件的总大小,则肯定是页面高速缓存崩溃了。
这意味着工作负载(通常是单个应用程序加上system_server)所需的工作集大于设备上的页面缓存可用的内存量。结果,随着工作负载的一部分在页面缓存中获取所需的数据,将在不久的将来使用的另一部分将被驱逐并必须再次获取,从而导致问题再次发生,直到加载为止已经完成。当设备上没有足够的可用内存时,这是性能问题的根本原因。
没有万无一失的方法来修复页面缓存抖动,但是有几种方法可以尝试在给定设备上改善此问题。