decadv_2024_01
2024-12-01
Today was spent writing a basic addition to my desktop. I wanted a GPU load/VRAM usage tracker for a while, but was overall too busy for the last few months to actually get around to it.
For initial context, I run a desktop with an NVIDIA card, and it has a tool for fnagling the GPU in general, called nvidia-smi
.
What's important is that it hasn't changed for as long as I ran NVIDIA cards, and that in it's CLI output, it has statistics about the GPU that you're running:
+-----------------------------------------------------------------------------------------+
| NVIDIA-SMI 560.35.03 Driver Version: 560.35.03 CUDA Version: 12.6 |
|-----------------------------------------+------------------------+----------------------+
| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 2070 ... Off | 00000000:08:00.0 On | N/A |
| 0% 47C P8 23W / 215W | 2492MiB / 8192MiB | 1% Default |
| | | N/A |
+-----------------------------------------+------------------------+----------------------+
What I need here is:
- The GPU-util value, which is a percentage of how loaded the GPU is
- The total available memory
- The used memory
With the total and used mem available I can also get a percentage of how much memory is used up, which is what is relevant to be displayed for me.
I ended up writing this script to get the values:
#!/usr/bin/env bash
# Get the output of nvidia-smi
nvidia_output=$(nvidia-smi)
rel_line=$(echo "$nvidia_output" | awk 'NR==10' | tr -s ' ')
# assuming MiB for RAM
total_vram=$(echo "$rel_line" | cut -d ' ' -f 11 | sed 's/MiB//g' )
used_vram=$(echo "$rel_line" | cut -d ' ' -f 9 | sed 's/MiB//g' )
gpu_load=$(echo "$rel_line" | cut -d ' ' -f 13 | sed 's/\%//g')
arg="$1"
if [ "$1" -eq 0 ]; then
echo "$total_vram"
fi
if [ "$1" -eq 1 ]; then
echo "$used_vram"
fi
if [ "$1" -eq 2 ]; then
echo "$gpu_load"
fi
It ain't clean but it's surprisingly reliable.
Incorporating the script into my desktop
With that, I just need to have this incorporated into my desktop. I use eww for my status bars, which uses a lisp dialect, called yuck and a gtk-style CSS stylesheet for it's configuration.
First, i needed to actually acquire the GPU load values into an accessible variable within eww's config. I ended up using defpoll
for this.
;; gpu load var
(defpoll gpu_vram_v :interval "1s" :initial "1" `bash gpu_load.sh 1`)
;; NOTE: only needs to run once
(defpoll gpu_vram_total_v :interval "999s" :initial "100" `bash gpu_load.sh 0`)
(defpoll gpu_load_v :interval "1s" :initial "0" `bash gpu_load.sh 2`)
With these values there, I just needed to define the right elements within the bar itself:
(box
:class "weird_base"
:height "48"
:width "2"
:valign "center"
:halign "center"
:orientation "h"
(box
:class "gpubar_base"
:height "48"
:width "2"
:valign "end"
:halign "center"
:hexpand false
(box
:class "${gpu_load_v > 95 ? "gpubar_warning" : "gpubar_active_portion"}"
:height "${round((gpu_vram_v/gpu_vram_total_v)*48, 0)}"
:width "2"
:hexpand false
:vexpand false
:halign "center"
:valign "end"
)
)
(box :width "4")
(box
:class "gpubar_base"
:height "48"
:width "2"
:valign "end"
:halign "center"
:hexpand false
(box
:class "${gpu_load_v > 95 ? "gpubar_warning" : "gpubar_active_portion"}"
:height "${round((gpu_load_v/100)*48, 0)}"
:width "2"
:hexpand false
:vexpand false
:halign "center"
:valign "end"
)
)
)
(label
:class "${gpu_load_v > 95 ? "gpu_title_warn" : "gpu_title"}"
:angle "90"
:text "${gpu_load_v > 95 ? "GPU WARN" : "GPU"}"
)
The main relevant portion of the code above is the percentage-based element height setting and the use of ternary statements for changing the class or content of an element in reaction to the input values.
The rest was simply getting things to look right through CSS.
Incoming: [decadv_2024_02] [decadv_2024_init]