summaryrefslogtreecommitdiffstats
path: root/tools/cuda-ab/ab.sh
blob: 07f6b246f2b5908d08c28284ec6c2a535519804a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/bin/bash
# SPDX-License-Identifier: BSD-3-Clause
#
# ab.sh - CUDA A/B harness: run the same workload on the host (bare metal,
# real Turing GPU) and in the guest (Open-IOV emulated vGPU), then compare the
# machine-readable results. Mirrors the nvidia-smi host/guest comparison used
# earlier: the host run is the oracle.
#
# Usage:
#   tools/cuda-ab/ab.sh host     # build + run on the host, save run/host.txt
#   tools/cuda-ab/ab.sh guest    # push the binary + run in the running VM
#   tools/cuda-ab/ab.sh compare  # diff the stable result lines
#   tools/cuda-ab/ab.sh all      # host + guest + compare
#
# The binary links the CUDA runtime statically, so the guest needs no CUDA
# toolkit - only the guest driver's libcuda.so.
set -e

HERE="$(cd "$(dirname "$0")" && pwd)"
REPO="$(cd "$HERE/../.." && pwd)"
VMEXEC="$REPO/scripts/vgpu_vm.py"
RUN="$HERE/run"
SRC="$HERE/vector_add.cu"
BIN="$RUN/vector_add"

mkdir -p "$RUN"

build_host() {
	command -v nvcc >/dev/null || { echo "nvcc not found (install nvidia-cuda-toolkit)"; exit 1; }
	nvcc -O2 --cudart=static -o "$BIN" "$SRC"
}

run_host() {
	build_host
	echo "[*] host run (oracle)"
	"$BIN" | tee "$RUN/host.txt"
}

push_bin() {
	[ -x "$BIN" ] || build_host
	local b64 chunk off total size
	b64=$(base64 -w0 "$BIN")
	total=${#b64}
	off=0
	size=70000
	echo "[*] pushing $(stat -c%s "$BIN") bytes to guest"
	"$VMEXEC" exec "rm -f /tmp/vadd.b64" >/dev/null 2>&1
	while [ $off -lt $total ]; do
		chunk=${b64:$off:$size}
		"$VMEXEC" exec "echo -n '$chunk' >> /tmp/vadd.b64" >/dev/null 2>&1
		off=$((off + size))
	done
	"$VMEXEC" exec "base64 -d /tmp/vadd.b64 > /tmp/vector_add && chmod +x /tmp/vector_add && rm -f /tmp/vadd.b64 && echo PUSH_OK"
}

run_guest() {
	push_bin
	echo "[*] guest run (emulated vGPU)"
	"$VMEXEC" exec "/tmp/vector_add" 2>&1 | tee "$RUN/guest.txt"
}

compare() {
	local h g
	[ -f "$RUN/host.txt" ] || { echo "missing $RUN/host.txt (run host first)"; exit 1; }
	[ -f "$RUN/guest.txt" ] || { echo "missing $RUN/guest.txt (run guest first)"; exit 1; }
	h=$(grep -E '^(DEVICE|RESULT|STATUS)' "$RUN/host.txt")
	g=$(grep -E '^(DEVICE|RESULT|STATUS)' "$RUN/guest.txt")
	echo "--- host:";  echo "$h"
	echo "--- guest:"; echo "$g"
	# DEVICE lines carry the GPU name (differs by design); compare the rest.
	h_norm=$(echo "$h" | sed 's/^DEVICE .*/DEVICE <gpu>/')
	g_norm=$(echo "$g" | sed 's/^DEVICE .*/DEVICE <gpu>/')
	if [ "$h_norm" = "$g_norm" ]; then
		echo "A/B: MATCH"
	else
		echo "A/B: MISMATCH"
		diff <(echo "$h_norm") <(echo "$g_norm") || true
		exit 1
	fi
}

case "${1:-all}" in
host)    run_host ;;
guest)   run_guest ;;
compare) compare ;;
all)     run_host; run_guest; compare ;;
*)       echo "usage: $0 [host|guest|compare|all]"; exit 1 ;;
esac