We build a lot of code for Apple platforms at Google, and spend a fair amount of time trying to optimize our builds. We have noticed that Macs are quite sensitive to thermals, and have done some work attempting to track the thermal characteristics of our machines and how that affects their build speeds. Here’s some info that we discovered that may be useful in your own explorations. The intended audience for this is someone quite familiar with macOS programming.
Apple has a plethora of APIs to monitor thermals on their platforms. Warning, your mileage may vary… Note also that I am focusing on userspace APIs. Kernel hackers have many more fun exciting and barely documented ways to get at thermal info.
IOPMGetThermalWarningLevel appears to have been introduced in macOS 10.7 and is documented to return one of three values:
The implementation for
IOPMGetThermalWarningLevel depends on
SCDynamicStore looking for the ‘
/IOKit/Power/ThermalWarning’ key. As far as I can tell, there is nothing that sets this key in System Configuration on macOS 10.14/10.15 so this call is useless (filed as Feedback FB7528845).
IOGetSystemLoadAdvisory and IOCopySystemLoadAdvisoryDetailed
IOCopySystemLoadAdvisoryDetailed appear to have been introduced in macOS 10.6.
IOGetSystemLoadAdvisory returns a simple value that is not a thermal measurement, but takes thermals into consideration.
IOCopySystemLoadAdvisoryDetailed returns a dictionary containing the ‘
kIOSystemLoadAdvisoryThermalLevelKey’ key. This key appears to contain one of these values:
The implementation for
IOCopySystemLoadAdvisoryDetailed also depends on
SCDynamicStore, in this case looking for the ‘
/IOKit/PowerManagement/SystemLoad/Detailed’ key. Contrary to the ThermalWarning key above, this is set.
NSProcessInfo contains some useful thermal related functionality. You can register for the
NSProcessInfoThermalStateDidChangeNotification, which alerts you to when you should check
[[NSProcessInfo currentProcess] thermalState] for updates.
-thermalState is documented to return the following values:
This notification does work on macOS 10.15 and has been around since macOS 10.10.3.
The very hopeful sounding OSThermalNotification.h appears at first glance to only support Apple’s mobile platforms. If you look carefully though you will see
kOSThermalNotificationPressureLevelName which first appeared in macOS 10.10. It is just a string constant to be used with Darwin Notifications. See below for details.
Darwin Notifications [aka notify(3)]
The Darwin notification system has been around since macOS 10.3. It has a very detailed man page:
man 3 notify. There are a couple of notification strings scattered around the headers that may be useful.
The most promising notification in IOKit is
kIOPMThermalWarningNotificationKey. It’s likely that this was intended to be used to notify you when to call
IOPMGetThermalWarningLevel. Unfortunately this notification never gets sent (as far as I can tell).
Other possibly useful notifications from IOKit are
kIOPMCPUPowerNotificationKey which alert you when to check
IOPMCopyCPUPowerStatus respectively. Note that if you don’t want to get the system load details for
kIOSystemLoadAdvisoryNotifyName you can call
notify_get_state to get the same value as
OSThermalNotification.h we have
kOSThermalNotificationPressureLevelName which returns one of the following values using
As it turns out this is what
NSProcessInfoThermalStateDidChangeNotification uses under the hood, and the constants appear to map:
Notifications are sent through
/usr/sbin/notifyd and are posted by
So how can we test macOS thermal code? (short of wrapping your machine up in a jacket…which we did for a while). With Xcode 11.3 Apple has introduced ways to force thermal profiles on iOS devices, but nothing exists for the Mac.
It turns out that there are a variety of command line utilities that will give you information about thermals:
Returns a huge amount of information about power and thermals. Probably overkill in most cases.
pmset -g thermlog
IOCopySystemLoadAdvisoryDetailed continuously. Since
IOPMGetThermalWarningLevel doesn't do anything useful, this is mainly only good for getting a continuously updated number for CPU Speed limit.
thermal does not have a man page, but running it without any arguments gives some basic “help”. There’s some real goodness in here allowing you to simulate thermal pressure notifications, get current levels and get the thermal configuration for your machine. It turns out that these configurations are all stored in
/System/Library/Extensions/IOPlatformPluginFamily.kext/Contents/PlugIns/X86PlatformPlugin.kext/Contents/Resources/ and are keyed by a per motherboard UUID. The configurations define the levels at which notifications are going to get sent.
For what it’s worth I have never been able (willing) to force my machines to report any real value higher than
kOSThermalPressureLevelModerate which is pretty surprising given that I have gotten my CPU Speed Limits down below 50. As mentioned above I had my MacBook Pro 16" doing a full build of YouTube while running two YouTube videos while wrapped in a fleece jacket, and that wasn’t sufficient to push the thermal levels above 120 (according to
thermal levels). My fans were going full bore, I was afraid of doing damage, and my office mates were getting annoyed. According to Apple at WWDC 2015 you only need to worry once you reach
NSProcessInfoThermalStateSerious and above, but in my experience so far you are unlikely to ever reach these levels.
Use notify with
kOSThermalNotificationPressureLevelName (or the higher level
NSProcessInfoThermalStateDidChangeNotification). Test using