commit 817cac2ddcab544be5c78fdfbf1fbecfd041ff32 Author: Bruno Silva Date: Thu Aug 27 12:12:58 2026 +0100 Script assisted by Claude diff --git a/keep-alive.py b/keep-alive.py new file mode 100644 index 0000000..460d0cb --- /dev/null +++ b/keep-alive.py @@ -0,0 +1,118 @@ +""" +gpu_keepalive.py + +Tiny AMD GPU keep-alive utility for Windows/WDDM VRAM eviction issues +(e.g. llama.cpp / llama-server on RX 7900 XTX + Adrenalin drivers). + +What it does: + - Opens an OpenCL context on the AMD GPU + - Allocates a small buffer resident in GPU memory + - Runs a trivial kernel against it on a timer + - This keeps the GPU context "active" so the driver's idle + power-management / residency-trim path doesn't decide to + evict allocations to system RAM. + +This does NOT touch llama.cpp or llama-server directly. It just +keeps the card from ever reaching the idle state that triggers +eviction, so llama-server's own VRAM allocation is less likely +to get trimmed. + +Requirements: + pip install pyopencl + +Usage: + python gpu_keepalive.py # defaults: 4s interval, 64MB buffer + python gpu_keepalive.py --interval 3 --size-mb 128 + python gpu_keepalive.py --list # list available OpenCL devices + +Stop with Ctrl+C. +""" + +import argparse +import sys +import time + +try: + import pyopencl as cl + import numpy as np +except ImportError: + print("Missing dependencies. Install with:\n pip install pyopencl numpy") + sys.exit(1) + + +KERNEL_SRC = """ +__kernel void touch(__global float* buf) { + int i = get_global_id(0); + buf[i] = buf[i] + 1.0f; +} +""" + + +def find_amd_device(): + for platform in cl.get_platforms(): + for device in platform.get_devices(): + if device.type & cl.device_type.GPU and "amd" in (platform.name + device.name).lower(): + return platform, device + # fallback: just grab the first GPU device found + for platform in cl.get_platforms(): + for device in platform.get_devices(): + if device.type & cl.device_type.GPU: + return platform, device + return None, None + + +def list_devices(): + for platform in cl.get_platforms(): + print(f"Platform: {platform.name}") + for device in platform.get_devices(): + print(f" Device: {device.name} ({device.type})") + + +def main(): + parser = argparse.ArgumentParser(description="Tiny AMD GPU keep-alive to prevent WDDM VRAM eviction.") + parser.add_argument("--interval", type=float, default=4.0, help="Seconds between GPU touches (default: 4)") + parser.add_argument("--size-mb", type=int, default=64, help="Size of the resident buffer in MB (default: 64)") + parser.add_argument("--list", action="store_true", help="List OpenCL platforms/devices and exit") + args = parser.parse_args() + + if args.list: + list_devices() + return + + platform, device = find_amd_device() + if device is None: + print("No OpenCL GPU device found. Is the AMD OpenCL runtime installed (it ships with Adrenalin)?") + sys.exit(1) + + print(f"Using platform: {platform.name}") + print(f"Using device: {device.name}") + + ctx = cl.Context([device]) + queue = cl.CommandQueue(ctx) + program = cl.Program(ctx, KERNEL_SRC).build() + kernel = cl.Kernel(program, "touch") + + n_floats = (args.size_mb * 1024 * 1024) // 4 + host_buf = np.zeros(n_floats, dtype=np.float32) + mf = cl.mem_flags + gpu_buf = cl.Buffer(ctx, mf.READ_WRITE | mf.COPY_HOST_PTR, hostbuf=host_buf) + + print(f"Resident buffer: {args.size_mb} MB") + print(f"Touch interval: {args.interval}s") + print("Running. Ctrl+C to stop.\n") + + try: + tick = 0 + while True: + kernel.set_args(gpu_buf) + cl.enqueue_nd_range_kernel(queue, kernel, (n_floats,), None) + queue.finish() + tick += 1 + print(f"\r[keep-alive] touched GPU buffer #{tick}", end="", flush=True) + time.sleep(args.interval) + except KeyboardInterrupt: + print("\nStopped.") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e11fe7a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyopencl +numpy \ No newline at end of file