Monitor Apple Silicon GPU on macOS with macmon + Hosted Graphite

Monitor Apple Silicon GPU on macOS with macmon + Hosted Graphite

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 .

Monitor Apple Silicon GPU on macOS with macmon + Hosted Graphite - 1

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 &

If you don't already have a Hosted Graphite account, sign up for a free trial here to obtain a Hosted Graphite API key.

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:

Monitor Apple Silicon GPU on macOS with macmon + Hosted Graphite - 2

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!

You might also like other posts...
metricfire Aug 25, 2025 · 7 min read

Visualize Logs Alongside Metrics: Complete Observability for Slow PostgreSQL Queries

In this guide you’ll enable slow-query logging, make the logs readable for MetricFire's Hosted... Continue Reading

metricfire Aug 22, 2025 · 10 min read

Why Am I Seeing NGINX 502 Bad Gateway: PHP-FPM?

Are you seeing Error 502: Bad Gateway on a website? Find out why you... Continue Reading

metricfire Aug 21, 2025 · 6 min read

Nginx Performance Monitoring: Loki Logs and Telegraf Metrics

Nginx is a crucial part of any stack, so pairing time-series metrics with corresponding... Continue Reading

header image

We strive for 99.95% uptime

Because our system is your system.

14-day trial 14-day trial
No Credit Card Required No Credit Card Required