Posted on
Artificial Intelligence

Power Optimisation for Artificial Intelligence Servers

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Power Optimisation for Artificial Intelligence Servers (with Bash you can trust)

AI servers eat watts for breakfast. Between multi-socket CPUs and data-center GPUs, a single node can draw more than a household. That’s money, heat, and carbon. The good news: with a few practical, scriptable Linux/Bash tweaks, you can lower power draw and improve energy per training/inference job—often with negligible impact on throughput.

This guide explains why power optimisation matters, then walks you through actionable steps you can automate on your fleet. All examples are Linux-first, Bash-friendly, and include apt/dnf/zypper install instructions where needed.


Why this matters (and why now)

  • Cost and capacity: Power-hungry nodes limit rack density and balloon electricity and cooling bills.

  • Performance stability: Throttling from thermal/power limits tanks throughput at the worst moments.

  • Sustainability and compliance: Energy per model/epoch is increasingly a KPI; regulators and customers are watching.


Before you start: Install the essentials

We’ll use common, vendor-neutral tools and optional GPU utilities. Run the appropriate commands for your distro.

  • powertop (measure and suggest tunings)

  • tuned (system-wide power/perf profiles)

  • cpupower or cpufrequtils (CPU frequency/governor controls)

  • numactl (NUMA awareness and pinning)

  • powercap-utils (optional, RAPL power capping on Intel)

  • NVIDIA users: nvidia-smi (comes with the proprietary driver)

Install system tools:

Debian/Ubuntu (apt):

sudo apt update
sudo apt install -y powertop tuned numactl powercap-utils ethtool
# cpupower lives in linux-tools; try generic first, then kernel-specific:
sudo apt install -y linux-tools-common linux-tools-generic || sudo apt install -y linux-tools-$(uname -r) cpufrequtils

Fedora/RHEL/CentOS (dnf):

sudo dnf install -y powertop tuned numactl powercap-utils ethtool kernel-tools
# cpupower is part of kernel-tools

openSUSE Leap/Tumbleweed (zypper):

sudo zypper refresh
sudo zypper install -y powertop tuned numactl powercap-utils ethtool cpupower

Enable tuned (all distros):

sudo systemctl enable --now tuned

NVIDIA driver (to get nvidia-smi):

  • Ubuntu/Debian (apt):
sudo apt update
sudo ubuntu-drivers autoinstall
sudo reboot
nvidia-smi
  • Fedora (dnf) via RPM Fusion:
sudo dnf install -y https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm \
                    https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
sudo dnf install -y akmod-nvidia
sudo reboot
nvidia-smi
  • openSUSE (zypper) using NVIDIA repo (example for Tumbleweed; replace if on Leap):
sudo zypper addrepo --refresh https://download.nvidia.com/opensuse/tumbleweed NVIDIA
sudo zypper install -y nvidia-computeG06
sudo reboot
nvidia-smi

Note: Package names and repos vary by GPU generation and distro release. Use your distro’s official NVIDIA guide if the above differs.


Step 1 — Measure a baseline like an SRE

You can’t optimise what you don’t measure. Capture idle and load numbers for power, clocks, and util.

  • CPU/system view:
sudo powertop --time=15s
sudo powertop --html=/tmp/power.html
xdg-open /tmp/power.html 2>/dev/null || echo "Report at /tmp/power.html"
  • NVIDIA GPUs:
nvidia-smi
nvidia-smi dmon -s pucvmet -o DT
# p: power, u: SM util, c: clocks, v: ECC, m: mem util, e: encoder, t: temperature

Tips:

  • Record idle vs training/inference loads.

  • If you have a smart PDU/UPS, log wall power too; it captures PSU and fan overheads.


Step 2 — Tune CPU governors, EPP, and turbo

Right-sizing CPU clocks can cut tens of watts with minimal hit to GPU-bound training or inference.

  • Inspect and set the CPU governor:
sudo cpupower frequency-info | sed -n '1,120p'
sudo cpupower frequency-set -g powersave     # often best for GPU-bound jobs
# For CPU-bound preprocessing, try performance or schedutil and compare energy/throughput.
  • Prefer energy-aware performance using EPP (Intel/AMD pstate):
# Show current EPP value (one CPU as example)
cat /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference 2>/dev/null || echo "EPP not available"

# Set all CPUs to balance_performance (good default), or powersave on inference nodes:
for p in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
  [ -f "$p" ] && echo balance_performance | sudo tee "$p" >/dev/null
done
  • Consider disabling turbo for predictable thermals (verify for your CPU):
# Intel (older intel_pstate)
[ -f /sys/devices/system/cpu/intel_pstate/no_turbo ] && echo 1 | sudo tee /sys/devices/system/cpu/intel_pstate/no_turbo

# Generic (some AMD/Intel)
[ -f /sys/devices/system/cpu/cpufreq/boost ] && echo 0 | sudo tee /sys/devices/system/cpu/cpufreq/boost

When to use which:

  • Training heavy on GPU: powersave governor + balance_performance EPP is often ideal.

  • CPU-heavy data loading: consider performance governor during that phase only (wrap your launcher).

Example wrapper:

#!/usr/bin/env bash
set -euo pipefail
sudo cpupower frequency-set -g performance
python train.py "$@"
sudo cpupower frequency-set -g powersave

Step 3 — Cap GPU power and stabilise clocks

Most AI workloads are not perfectly power-linear. Modest GPU caps (80–90% of TDP) often reduce watts significantly with small throughput loss. Always test on your model.

  • Enable persistence and view limits:
sudo nvidia-smi -pm 1
nvidia-smi -q -d POWER | sed -n '1,120p'
  • Set a power limit (per GPU or all GPUs):
# Example: cap to 260W per GPU (adjust for your model)
for i in $(nvidia-smi --query-gpu=index --format=csv,noheader); do
  sudo nvidia-smi -i "$i" -pl 260
done
  • Optionally lock clocks for consistency (adjust values to what your GPU supports):
# Lock graphics clocks to a range (query supported with: nvidia-smi -q -d SUPPORTED_CLOCKS)
sudo nvidia-smi -lgc 1200,1500

# Lock memory clocks (example; values must match your GPU's supported list)
sudo nvidia-smi -lmc 877,877

# Reset when done:
sudo nvidia-smi -rgc
sudo nvidia-smi -rmc
  • Monitor under load:
nvidia-smi dmon -s pucvmet -o DT

Practical guidance:

  • Start at 90% of TDP, observe throughput (images/s, tokens/s) and energy per job, then step down by 10–20W.

  • For latency-critical inference, prefer consistent clocks over aggressive caps.


Step 4 — Pin workloads to the right NUMA node

Cross-socket memory traffic and IRQ migrations cost both performance and power. Keep your process near its GPU.

  • Find GPU PCI bus and NUMA node:
GPU_BUS=$(nvidia-smi --query-gpu=pci.bus_id --format=csv,noheader | sed -n '1p' | sed 's/^0000://')
echo "GPU0 PCI bus: $GPU_BUS"
NODE=$(cat /sys/bus/pci/devices/0000:$GPU_BUS/numa_node)
echo "GPU0 NUMA node: $NODE"
  • Run your job with CPU and memory bound to that node:
numactl --cpunodebind="$NODE" --membind="$NODE" python train.py
  • You can also pin with taskset if you know core lists:
taskset -c 0-15 python infer.py

This simple locality tweak often reduces CPU package power and shaves milliseconds off hot paths.


Step 5 — Make it stick: tuned profiles and powertop auto-tune

Lock in safe defaults and let SREs flip profiles per node role.

  • Try tuned profiles:
sudo tuned-adm profile balanced
# Other options to evaluate:
# sudo tuned-adm profile throughput-performance
# sudo tuned-adm profile latency-performance
# sudo tuned-adm profile powersave
  • Apply powertop recommendations at boot (be selective in production):
sudo tee /etc/systemd/system/powertop.service >/dev/null <<'EOF'
[Unit]
Description=Powertop tunings
After=multi-user.target

[Service]
Type=oneshot
ExecStart=/usr/sbin/powertop --auto-tune

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now powertop.service

Caution: powertop --auto-tune can put some NICs/storage into aggressive power states. Validate with your NIC/storage vendor guidance and your latency SLOs.


Optional — CPU package power capping (Intel RAPL)

If your facility enforces rack power ceilings, RAPL can keep CPUs under a steady envelope.

  • Discover domains:
ls /sys/class/powercap/intel-rapl:0/
  • Set a short-term limit (value in microwatts):
# Example: 125W package limit
echo 125000000 | sudo tee /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw
  • Verify:
cat /sys/class/powercap/intel-rapl:0/constraint_0_power_limit_uw

Note: Names and constraints vary by CPU generation; test for throughput impact on data pipelines.


What “good” looks like

  • GPU-bound training: single-digit throughput loss for double-digit power savings is common when capping GPUs to 80–90% TDP—especially if data loading isn’t CPU-throttled.

  • Inference: tighter clocks and NUMA pinning often improve latency stability while lowering average watts.

  • Fleet view: the biggest wins come from repeatable profiles and automation, not hero-tuning per node.


Common pitfalls

  • Over-tuning powertop: can hurt NIC latency/throughput. Exclude critical devices if needed.

  • Forgetting persistence: GPU caps/clocks reset on reboot or driver reload; codify them in systemd or your node init.

  • Ignoring thermals: bad airflow negates tuning; watch inlet/outlet temps and fan curves.


Quick “put it together” script

Example: cap GPUs, set CPU for GPU-bound workloads, bind to the nearest NUMA node, run job, then restore.

#!/usr/bin/env bash
set -euo pipefail

# 1) CPU toward energy-efficient for GPU-bound job
sudo cpupower frequency-set -g powersave || true
for p in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do
  [ -f "$p" ] && echo balance_performance | sudo tee "$p" >/dev/null
done

# 2) GPU caps (adjust per your hardware)
sudo nvidia-smi -pm 1 || true
for i in $(nvidia-smi --query-gpu=index --format=csv,noheader); do
  sudo nvidia-smi -i "$i" -pl 260 || true
done

# 3) Bind to the NUMA node nearest GPU0
GPU_BUS=$(nvidia-smi --query-gpu=pci.bus_id --format=csv,noheader | sed -n '1p' | sed 's/^0000://')
NODE=$(cat /sys/bus/pci/devices/0000:$GPU_BUS/numa_node)
echo "Running on NUMA node $NODE"

# 4) Launch workload
exec numactl --cpunodebind="$NODE" --membind="$NODE" "$@"

Run:

chmod +x run_energy_efficient.sh
./run_energy_efficient.sh python train.py --config config.yaml

Conclusion and next steps

Every watt you save without hurting SLA/throughput makes your AI platform cheaper, cooler, and greener. Start with measurement, right-size CPU and GPU power, keep workloads local to their GPUs, and lock in sane defaults with tuned and systemd. Then:

  • Automate: bake these steps into your provisioning (cloud-init/Ansible/Kubernetes DaemonSets).

  • Track: monitor energy per job/model as a first-class metric.

  • Iterate: each model/dataset behaves differently; keep simple toggles to A/B caps and governors.

If you’d like, I can help you turn these snippets into a hardened bootstrap script for your exact CPU/GPU mix and distro.