Thinkpad X1 Carbon GPU Undervolting in Arch Linux

After successfully installing Arch Linux on my Thinkpad X1 Carbon Generation 6 I began experimenting with laptop component undervolting to reduce heat and improve performance. Unfortunately it seems that the set points are being ignored to some extent.

Indeed, Francesco Palmarini reported on Github:

It is kind of impossible that your GPU can work at -1V offset voltage. On XTU I get an instant reboot on -500mV

However, despite the apparent physical impossibility of applying such a large voltage offset, there seems to be a real effect on performance. Applying a -500 mV voltage offset resulted in approximately 2 watts lower peak power consumption as measured by throttled monitoring.

I benchmarked GPU performance at different voltage offsets to see if they are ignored after a certain point. The result shows diminishing returns beyond 300 millivolt undervoltage. Still, it is not clear whether the larger undervoltage settings are actually being set correctly because it should not be possible for the CPU to operate when undervolted 900 mV.

# config.toml

[voltage]
start = 0
stop = -1000
step = -100
# test.py

import toml
from subprocess import run
from multiprocessing import Process
from tempfile import NamedTemporaryFile
from jinja2 import Template
from tqdm import tqdm

def undervolt_gpu(voltage):
    log = f"undervolt___{voltage}.log"
    print(f"undervolt_gpu: saving to {log}")
    with open("lenovo_fix.conf") as fd:
        tpl = Template(fd.read()).render(voltage=voltage)
    with NamedTemporaryFile(mode="w") as ntf:
        ntf.write(tpl)
        ntf.flush()
        cmd = f"sudo /usr/lib/throttled/lenovo_fix.py --monitor --log {log} --config {ntf.name}"
        print("voltage", voltage, "cmd", cmd)
        run(cmd, check=True, shell=True)

if __name__ == "__main__":
    cfg = toml.load("config.toml")
    voltages = list(range(cfg["voltage"]["start"], cfg["voltage"]["stop"], cfg["voltage"]["step"]))
    print(voltages)
    for voltage in tqdm(voltages):
        p = Process(target=undervolt_gpu, args=(voltage,))
        p.start()
        cmd = f"glmark2 -b :duration=2.0 --fullscreen | tee glmark___{voltage}.log"
        print("process launched")
        run(cmd, check=True, shell=True)
        print("process terminating")
        p.terminate()
        print("process terminated")

I generated a CSV report by regex parsing the data logged from glmark2 and lenovo_fix.py:

# report.py

import re
import os.path
import toml
import csv

cfg = toml.load("config.toml")
voltages = list(range(cfg["voltage"]["start"], cfg["voltage"]["stop"], cfg["voltage"]["step"]))

with open('undervolting.csv', 'w', newline='') as csvfile:
    fieldnames = ['glmark', 'voltage', 'package_watts', 'graphics_watts', 'dram_watts']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
    writer.writeheader()
    for voltage in voltages:
        glmark_p = f"glmark___{voltage}.log"
        undervolt_p = f"undervolt___{voltage}.log"
        try:
            with open(glmark_p) as fd:
                try:
                    glmark_s = re.search("(?:glmark2 Score: )(\d+)", fd.read()).group(1)
                except AttributeError:
                    continue
        except FileNotFoundError:
            continue
        try:
            with open(undervolt_p) as fd:
                for line in fd:
                    package, graphics, dram = getattr(re.search(
                        "(?:Package: )(\d*\.?\d+)(?: W - Graphics: )(\d*\.?\d+)(?: W - DRAM: )(\d*\.?\d+)", line
                    ), "groups", lambda: ("0", "0", "0",))()
                    writer.writerow(dict(
                        glmark=glmark_s,
                        voltage=voltage,
                        package_watts=package,
                        graphics_watts=graphics,
                        dram_watts=dram))
        except FileNotFoundError:
            continue

throttled GPU voltage offset adjustment has some effect on GPU performance, but it is unclear to what extent this is meaningful with the Linux implementation. GPU voltage offset adjustment parameters are not on the same scale as those within the Windows-based Intel XTU utility. The relationship between throttled GPU voltage offset and Intel XTU voltage offset warrants further research to fully unlock the battery life and calculation performance potential of our machines.

To set your GPU voltage offset to -400 mV, edit lenovo_fix.conf which is located at /etc/lenovo_fix.conf on Arch Linux if you installed throttled from the throttled package.