解决MacBook Pro 2019外接屏幕时变慢问题
背景
我的主力工作机(Macbook Pro 2019)近半年来每到下午2点气温升高时(在家工作,乡下地方没装空调)执行CPU密集工作,例如编译或者进行多人Zoom会议,就会变得非常慢。
早期因为用Vim做主力开发工具还能凑合(Intellij和VsCode只能偶尔在上午和晚上用),但最近会议增多经常要挂着zoom同时工作(偶尔编译),实在忍无可忍,于是今天好好研究了一把,最终解决了这个问题。这里把解决方案记录一下。注意解决过程中尝试了不同的方法,有些可能并不是关键因素但理论上对性能会有帮助,我就继续保留并没有恢复到原始状态,最后虽然成功但目前也没有确定究竟是哪几个步骤在真正起作用,留待以后有空再研究吧。
首先从现象上,确认了是外接显示器的问题,准确来说是双屏的问题。单独使用外接显示器(合上笔记本)或者单独使用内置屏幕均没有问题。之前一直没有往这方面想,但上网查了查似乎不少人都遇到了这个问题。
当然最直接的解决办法是只使用外接显示器,但这样一来内置麦和摄像头都没法用了。
使用镜像双屏能一定程度上缓解,但仍然会经常变慢,只不过比分屏要好一点。
监控和重现
为了加快解决效率,需要更加精确有效一点的监控手段。留意到变慢时
kernal_task
进程占用大量CPU,而
kernal_task
的主要作用(之一)就是抢占CPU将其闲置来降温。因此使用以下命令来监控CPU的温度和
kernal_task
的CPU占用率。
sudo powermetrics --samplers smc,tasks |grep -i "die temperature\|kernel_task"
下图是一个典型结果
在一个编译任务执行过程中,
kernel_task
的CPU占用量飙到6000+ (第二列,第一列的0是pid)。合上笔记本后,瞬间降到100以下最终降到30左右,而整个过程中CPU温度其实并没有明显变化。
而合上笔记本只使用一个显示器时,即使CPU达到98度,
kernel_task
的CPU占用率也一直保持低位,而这时笔记本的操作仍然十分流畅。
如果使用以下命令监控CPU的限速情况
pmset -g thermlog
我留意到即使
CPU_Speed_Limit
十分低,但只要
CPU_Scheduler_Limit
高,界面操作就不会卡顿。当
kernel_task
开始占用CPU,这里的
CPU_Scheduler_Limit
也会开始下降,界面操作出现严重卡顿。
可以看到,双屏时出现卡顿的直接原因貌似不是因为CPU/GPU发热,而是
kernel_task
过早介入,不知是不是bug。
尝试解诀方案
如前所述,下面将按尝试顺序列出方案,这些方案理论上会对性能有改善因此在尝试后不管是否关键因素我也(暂时)没有改回来,最终的结果暂时只能看成是以下所有方案的综合效果。其中有一些步骤会改变系统的行为,暂时我只能说这个方案只适合于使用笔记本进行web和后端程序开发(文本操作为主)的场景。
1、限制spotlight和alfred的index目录
这是在发现双屏问题前的尝试,我留意到mdc(系统索引进程)经常占用一定的CPU。开发目录文件很零碎,对索引效率有很大影响,而我完全不用spotlight查找文件。因此可以把开发目录或者整个Home目录加到Spotlight的Privacy列表中。配置后确实可以减少mdc进程的cpu占用但对卡顿问题并无明显帮助。
2、使用独立显示器链接线并独占左侧接口
这是同事介绍的方案,不要用多合一的dock去连接显示器。使用单独的usb-c到hdmi/displayport转换线。并且笔记本同侧的另一个usb-c接口不要再接入其他东西。电源和dock连接到另外一侧的接口。
效果:有所改善,但仍会出现卡顿(特指CPU温度不太高但
kernel_task
的CPU率却飙升导致的卡顿,下同)。
(用前面的监控手段可以第一时间发现出现卡顿,但卡顿是突然出现的,对于每个可能的解决方案,只能观察到卡顿出现得越来越少,或者越来越晚,无法直接明确认定该方案就是解决卡顿的关键手段。)
3、强制禁用独立显卡
方案来自这个答案
简单来说就是执行
sudo pmset -a GPUSwitch 0
禁止系统尝试使用独立显卡。执行后用
pmset -g
确认(看
gpuswitch
项,应该为0)
然后在
电源
(
Energy Saver
)配置里反选自动切换显卡 (
Automatic graphics switching
)
效果:大幅改善,但偶尔仍会出现卡顿。
4、取消选中"显示器具有单独的空间"
来自这个答案
在
调度中心
(
Mission Control
)里反选
显示器具有单独的空间
(
Displays have separate Spaces
)。这个方案会导致所有分屏公用同一个“显示空间”,也就是调换了“屏幕”与“空间”的一对多层次关系。勾选时是一个屏幕上可以有多个空间,反选时是一个空间对应所有的屏幕。这个改动有两个明显的副作用,一是应用栏(docker)只在一个(主)屏幕上出现(你可以理解为每个空间只能有一个应用栏,而现在所有屏幕都在一个空间里);二是切换空间时所有屏幕都会联动(这也很好理解,因为现在是每个空间对应多个屏幕,你切换到下一个空间则所有屏幕都切换到该空间)。两种方案互有长短,纯看是否习惯了。我个人根本就不怎么用屏幕空间,所以对我毫无影响。
效果:大幅改善,进行此改动后再没看到过卡顿。但如果保留此改动,把前面第3点改回去,又会偶尔出现卡顿,因此至少3和4都是必须的。我并没有测试过保留3、4但把2改回去的情况。