Table of Contents
Introduction
Your Mac’s GPU is a massively parallel processor that handles anything from animating the UI to heavy lifting in video editors, 3D tools, games, and on-device machine learning models. Think Final Cut Pro exports, Blender renders, Stable Diffusion, WebGPU demos, or shader builds in Xcode - which are all tasks that require heavy GPU.
When the GPU is busy, a few practical things follow like the battery drains faster, the machine runs hotter. When the temperatures of your machine climbs, macOS can throttle clocks to protect the hardware, but monitoring the GPU gives you visibility into those tradeoffs. If you see high utilization with dropping frequency and rising temperature, that’s classic throttling. If power spikes at idle, maybe a background app is quietly using the GPU. With a few simple metrics you can correlate “the laptop feels sluggish” with real numbers to fix the root cause instead of guessing. This is where macmon and Hosted Graphite come in to give you better visibility into your operating system.
Step 1: Install Macmon on macOS
Macs with Apple Silicon (M1/M2/M3) put the CPU, GPU, Neural Engine, and media blocks on a single system on a chip (SOC) that shares high-bandwidth unified memory. That integrated GPU isn’t just for drawing the desktop, it’s an efficient compute device tuned for Metal and high performance-per-watt. Video editors lean on it for effects and exports, 3D apps push it for rendering, machine learning frameworks can offload kernels, and even browsers can exercise it via WebGPU. Because power, clocks, and thermals are all managed dynamically on the same chip, watching GPU metrics tells you when you’re compute-bound, memory-bandwidth-bound, or simply thermally throttled.
macmon streams these Apple-Silicon stats as newline-delimited JSON without sudo, kexts, or privileges. It exposes GPU power, frequency and utilization, temperatures, and memory fields, which are exactly what we need for meaningful performance monitoring. Just install it with Homebrew (as well as jq for JSON parsing):
brew install macmon jq
Confirm that macmon is collecting metrics from your macOS with the following command (you'll see keys like gpu_power, gpu_usage, temp.gpu_temp_avg):
macmon pipe | head -n 1 | jq .
Step 2: Forward GPU Metrics to Hosted Graphite
macmon emits newline-delimited JSON but Graphite expects simple plaintext lines in the form metric.name numeric-value [timestamp]. So we need a tiny bit of parsing to turn JSON fields (like gpu_power, gpu_usage) into Graphite lines, which is exactly what this script does. It reads one JSON sample from macmon, extracts a handful of metrics with jq, forwards them to Hosted Graphite’s public carbon endpoint using the netcat utility, sleeps 5 minutes, and repeats. It’s intentionally minimal with no retries, fancy batching, and just enough to get data flowing to an easy to use storage backend.
Create a new Bash file named macmon-metrics.sh and include the below script:
#!/bin/bash
apikey="<YOUR-HG-API-KEY>"
prefix="$apikey.macmon-metrics"
while true; do
macmon pipe | jq -c '.' | while read -r line; do
ts=$(echo "$line" | jq -r '.timestamp')
# Top-level system metrics
all_power=$(echo "$line" | jq -r '.all_power')
echo "$prefix.all_power $all_power" | nc carbon.hostedgraphite.com 2003
ane_power=$(echo "$line" | jq -r '.ane_power')
echo "$prefix.ane_power $ane_power" | nc carbon.hostedgraphite.com 2003
cpu_power=$(echo "$line" | jq -r '.cpu_power')
echo "$prefix.cpu_power $cpu_power" | nc carbon.hostedgraphite.com 2003
gpu_power=$(echo "$line" | jq -r '.gpu_power')
echo "$prefix.gpu_power $gpu_power " | nc carbon.hostedgraphite.com 2003
gpu_ram_power=$(echo "$line" | jq -r '.gpu_ram_power')
echo "$prefix.gpu_ram_power $gpu_ram_power " | nc carbon.hostedgraphite.com 2003
ram_power=$(echo "$line" | jq -r '.ram_power')
echo "$prefix.ram_power $ram_power" | nc carbon.hostedgraphite.com 2003
sys_power=$(echo "$line" | jq -r '.sys_power')
echo "$prefix.sys_power $sys_power" | nc carbon.hostedgraphite.com 2003
# CPU / ECPU / GPU usage arrays: [frequency, utilization]
ecpu_freq=$(echo "$line" | jq -r '.ecpu_usage[0]')
echo "$prefix.ecpu_frequency_hz $ecpu_freq" | nc carbon.hostedgraphite.com 2003
ecpu_util=$(echo "$line" | jq -r '.ecpu_usage[1]')
echo "$prefix.ecpu_utilization $ecpu_util" | nc carbon.hostedgraphite.com 2003
pcpu_freq=$(echo "$line" | jq -r '.pcpu_usage[0]')
echo "$prefix.pcpu_frequency_hz $pcpu_freq" | nc carbon.hostedgraphite.com 2003
pcpu_util=$(echo "$line" | jq -r '.pcpu_usage[1]')
echo "$prefix.pcpu_utilization $pcpu_util" | nc carbon.hostedgraphite.com 2003
gpu_freq=$(echo "$line" | jq -r '.gpu_usage[0]')
echo "$prefix.gpu_frequency_mhz $gpu_freq" | nc carbon.hostedgraphite.com 2003
gpu_util=$(echo "$line" | jq -r '.gpu_usage[1]')
echo "$prefix.gpu_utilization $gpu_util" | nc carbon.hostedgraphite.com 2003
# Memory
ram_total=$(echo "$line" | jq -r '.memory.ram_total')
echo "$prefix.memory_ram_total_bytes $ram_total" | nc carbon.hostedgraphite.com 2003
ram_usage=$(echo "$line" | jq -r '.memory.ram_usage')
echo "$prefix.memory_ram_usage_bytes $ram_usage" | nc carbon.hostedgraphite.com 2003
swap_total=$(echo "$line" | jq -r '.memory.swap_total')
echo "$prefix.memory_swap_total_bytes $swap_total" | nc carbon.hostedgraphite.com 2003
swap_usage=$(echo "$line" | jq -r '.memory.swap_usage')
echo "$prefix.memory_swap_usage_bytes $swap_usage" | nc carbon.hostedgraphite.com 2003
# Temperatures
cpu_temp=$(echo "$line" | jq -r '.temp.cpu_temp_avg')
echo "$prefix.temperature_cpu_celsius $cpu_temp" | nc carbon.hostedgraphite.com 2003
gpu_temp=$(echo "$line" | jq -r '.temp.gpu_temp_avg')
echo "$prefix.temperature_gpu_celsius $gpu_temp" | nc carbon.hostedgraphite.com 2003
break
done
sleep 300
done
Now just save the file, make it executable, and run it in the background (without logs):
chmod +x <path-to-file>/macmon-metrics.sh
nohup <path-to-file>/macmon-metrics.sh >/dev/null 2>&1 &
Step 3: Interpreting macOS GPU Metrics
The above script will collect and forward 19 system performance metrics, including some GPU stats - here's what they represent:
- gpu_utilization is the “how busy” number, reported as a fraction from 0 to 1. A render might peg this near 1.0; a mostly-CPU workflow will sit lower.
- gpu_frequency_mhz is the clock. When things get hot, you’ll often see frequency sag even while utilization stays high which indicates throttling. Pair frequency with temperature for the full picture.
- gpu_power (in watts) tells you how hard the GPU is being driven. Battery users care about this because 12–20 W sustained is a very different battery story than 3–5 W.
- temperature_gpu_celsius closes the loop. If temps are high and frequency drops while utilization stays elevated, you’re thermally limited. That’s your cue to tweak: reduce preview resolution, cap FPS, cool the chassis, adjust workload chunking, or just plan around sustained loads.
Here's what these macmon metrics look like when visualized in your Hosted Grafana dashboards:
Conclusion
This isn’t about graphs for the sake of graphs, it’s about closing the feedback loop in your day-to-day workflow. For example, if you’re cutting video, a timeline that “feels sticky” might show 95% GPU utilization with clocks sliding down after a minute, indicating classic thermal throttling. So now you can take action by lowering the preview quality, rendering shorter export chunks, or by simply giving the machine more airflow until you see the numbers stabilize. You don’t need heavyweight tooling to get this kind of visibility. macmon plus a tiny bash script gives you the GPU’s vital signs in an easy to understand time-series format. Once you’ve got the signals, tuning your workflow becomes less of a guess and more repeatable.
Reach out to MetricFire today and learn how their Hosted Graphite product can satisfy your monitoring requirements, and give you full visibility into any environment!