pantheon-gpu 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pantheon_gpu/Makefile +207 -0
- pantheon_gpu/VERSION +1 -0
- pantheon_gpu/__init__.py +1 -0
- pantheon_gpu/_entry.py +17 -0
- pantheon_gpu/kernels/all_reduce/README.md +17 -0
- pantheon_gpu/kernels/all_reduce/all_reduce.cpp +178 -0
- pantheon_gpu/kernels/allocation_fragmentation/README.md +3 -0
- pantheon_gpu/kernels/allocation_fragmentation/allocation_fragmentation.cpp +177 -0
- pantheon_gpu/kernels/atomic_virus/README.md +57 -0
- pantheon_gpu/kernels/atomic_virus/atomic_virus.cpp +266 -0
- pantheon_gpu/kernels/atomic_virus/atomic_virus_flow.svg +33 -0
- pantheon_gpu/kernels/cache_latency/README.md +57 -0
- pantheon_gpu/kernels/cache_latency/cache_latency.cpp +279 -0
- pantheon_gpu/kernels/cache_latency/cache_latency_flow.svg +30 -0
- pantheon_gpu/kernels/common/ai_workload_template.h +366 -0
- pantheon_gpu/kernels/common/common.h +427 -0
- pantheon_gpu/kernels/common/fault_log.h +136 -0
- pantheon_gpu/kernels/common/fp16_shim.h +20 -0
- pantheon_gpu/kernels/common/mock_gpu.h +212 -0
- pantheon_gpu/kernels/common/nvenc/nvEncodeAPI.h +4667 -0
- pantheon_gpu/kernels/compute_virus/README.md +59 -0
- pantheon_gpu/kernels/compute_virus/compute_virus.cpp +223 -0
- pantheon_gpu/kernels/compute_virus/compute_virus_agg.cpp +280 -0
- pantheon_gpu/kernels/compute_virus/compute_virus_flow.svg +36 -0
- pantheon_gpu/kernels/fp64_virus/README.md +96 -0
- pantheon_gpu/kernels/fp64_virus/fp64_virus.cpp +249 -0
- pantheon_gpu/kernels/fp64_virus/fp64_virus_flow.svg +95 -0
- pantheon_gpu/kernels/fused_attention/README.md +3 -0
- pantheon_gpu/kernels/fused_attention/fused_attention.cpp +2 -0
- pantheon_gpu/kernels/galpat/README.md +62 -0
- pantheon_gpu/kernels/galpat/galpat.cpp +233 -0
- pantheon_gpu/kernels/graph_replay/README.md +3 -0
- pantheon_gpu/kernels/graph_replay/graph_replay.cpp +173 -0
- pantheon_gpu/kernels/idle/README.md +34 -0
- pantheon_gpu/kernels/idle/idle.cpp +29 -0
- pantheon_gpu/kernels/idle/idle_flow.svg +11 -0
- pantheon_gpu/kernels/int_virus/README.md +57 -0
- pantheon_gpu/kernels/int_virus/int_virus.cpp +272 -0
- pantheon_gpu/kernels/int_virus/int_virus_flow.svg +32 -0
- pantheon_gpu/kernels/kv_cache_churn/README.md +20 -0
- pantheon_gpu/kernels/kv_cache_churn/kv_cache_churn.cpp +37 -0
- pantheon_gpu/kernels/llm_decode/README.md +25 -0
- pantheon_gpu/kernels/llm_decode/llm_decode.cpp +113 -0
- pantheon_gpu/kernels/llm_prefill/README.md +22 -0
- pantheon_gpu/kernels/llm_prefill/llm_prefill.cpp +55 -0
- pantheon_gpu/kernels/march_test/README.md +72 -0
- pantheon_gpu/kernels/march_test/march_test.cpp +243 -0
- pantheon_gpu/kernels/media_enc_virus/README.md +38 -0
- pantheon_gpu/kernels/media_enc_virus/media_enc_virus.cpp +267 -0
- pantheon_gpu/kernels/media_enc_virus/media_enc_virus_flow.svg +10 -0
- pantheon_gpu/kernels/memory_bank_thrash/README.md +42 -0
- pantheon_gpu/kernels/memory_bank_thrash/memory_bank_thrash.cpp +228 -0
- pantheon_gpu/kernels/memory_bank_thrash/memory_bank_thrash_flow.svg +10 -0
- pantheon_gpu/kernels/memory_cache_fracture/README.md +42 -0
- pantheon_gpu/kernels/memory_cache_fracture/memory_cache_fracture.cpp +223 -0
- pantheon_gpu/kernels/memory_cache_fracture/memory_cache_fracture_flow.svg +9 -0
- pantheon_gpu/kernels/memory_hammer/README.md +83 -0
- pantheon_gpu/kernels/memory_hammer/memory_hammer.cpp +299 -0
- pantheon_gpu/kernels/memory_pc_pingpong/README.md +42 -0
- pantheon_gpu/kernels/memory_pc_pingpong/memory_pc_pingpong.cpp +248 -0
- pantheon_gpu/kernels/memory_pc_pingpong/memory_pc_pingpong_flow.svg +9 -0
- pantheon_gpu/kernels/memory_read/README.md +69 -0
- pantheon_gpu/kernels/memory_read/memory_read.cpp +295 -0
- pantheon_gpu/kernels/memory_retention/README.md +67 -0
- pantheon_gpu/kernels/memory_retention/memory_retention.cpp +193 -0
- pantheon_gpu/kernels/memory_retention_bake/README.md +41 -0
- pantheon_gpu/kernels/memory_retention_bake/memory_retention_bake.cpp +245 -0
- pantheon_gpu/kernels/memory_retention_bake/memory_retention_bake_flow.svg +10 -0
- pantheon_gpu/kernels/memory_thermal_asym/README.md +41 -0
- pantheon_gpu/kernels/memory_thermal_asym/memory_thermal_asym.cpp +230 -0
- pantheon_gpu/kernels/memory_thermal_asym/memory_thermal_asym_flow.svg +7 -0
- pantheon_gpu/kernels/memory_tsv_thrasher/README.md +42 -0
- pantheon_gpu/kernels/memory_tsv_thrasher/memory_tsv_thrasher.cpp +242 -0
- pantheon_gpu/kernels/memory_tsv_thrasher/memory_tsv_thrasher_flow.svg +8 -0
- pantheon_gpu/kernels/memory_write/README.md +71 -0
- pantheon_gpu/kernels/memory_write/memory_write.cpp +262 -0
- pantheon_gpu/kernels/mma_virus/README.md +42 -0
- pantheon_gpu/kernels/mma_virus/mma_virus.cpp +288 -0
- pantheon_gpu/kernels/mma_virus/mma_virus_flow.svg +10 -0
- pantheon_gpu/kernels/moe_router/README.md +3 -0
- pantheon_gpu/kernels/moe_router/moe_router.cpp +2 -0
- pantheon_gpu/kernels/omni_virus/README.md +43 -0
- pantheon_gpu/kernels/omni_virus/omni_virus.cpp +375 -0
- pantheon_gpu/kernels/omni_virus/omni_virus_flow.svg +7 -0
- pantheon_gpu/kernels/p2p_thrasher/README.md +57 -0
- pantheon_gpu/kernels/p2p_thrasher/p2p_thrasher.cpp +280 -0
- pantheon_gpu/kernels/p2p_thrasher/p2p_thrasher_flow.svg +27 -0
- pantheon_gpu/kernels/pcie_bandwidth/README.md +56 -0
- pantheon_gpu/kernels/pcie_bandwidth/pcie_bandwidth.cpp +284 -0
- pantheon_gpu/kernels/pcie_bandwidth/pcie_bandwidth_flow.svg +19 -0
- pantheon_gpu/kernels/pulse_virus/README.md +56 -0
- pantheon_gpu/kernels/pulse_virus/pulse_virus.cpp +256 -0
- pantheon_gpu/kernels/pulse_virus/pulse_virus_flow.svg +18 -0
- pantheon_gpu/kernels/quantized_gemm/README.md +3 -0
- pantheon_gpu/kernels/quantized_gemm/quantized_gemm.cpp +2 -0
- pantheon_gpu/kernels/rag_embedding/README.md +3 -0
- pantheon_gpu/kernels/rag_embedding/rag_embedding.cpp +2 -0
- pantheon_gpu/kernels/ras_validator/README.md +41 -0
- pantheon_gpu/kernels/ras_validator/ras_validator.cpp +270 -0
- pantheon_gpu/kernels/ras_validator/ras_validator_flow.svg +10 -0
- pantheon_gpu/kernels/rope_stress/README.md +3 -0
- pantheon_gpu/kernels/rope_stress/rope_stress.cpp +2 -0
- pantheon_gpu/kernels/rt_virus/README.md +56 -0
- pantheon_gpu/kernels/rt_virus/rt_virus.cpp +567 -0
- pantheon_gpu/kernels/rt_virus/rt_virus_flow.svg +20 -0
- pantheon_gpu/kernels/scheduler_virus/README.md +42 -0
- pantheon_gpu/kernels/scheduler_virus/scheduler_virus.cpp +260 -0
- pantheon_gpu/kernels/scheduler_virus/scheduler_virus_flow.svg +9 -0
- pantheon_gpu/kernels/serving_mix/README.md +3 -0
- pantheon_gpu/kernels/serving_mix/serving_mix.cpp +2 -0
- pantheon_gpu/kernels/sfu_stress/README.md +57 -0
- pantheon_gpu/kernels/sfu_stress/sfu_stress.cpp +246 -0
- pantheon_gpu/kernels/sfu_stress/sfu_stress_flow.svg +31 -0
- pantheon_gpu/kernels/speculative_decode/README.md +3 -0
- pantheon_gpu/kernels/speculative_decode/speculative_decode.cpp +2 -0
- pantheon_gpu/kernels/tensor_virus/README.md +56 -0
- pantheon_gpu/kernels/tensor_virus/tensor_virus.cpp +251 -0
- pantheon_gpu/kernels/tensor_virus/tensor_virus_flow.svg +29 -0
- pantheon_gpu/kernels/tlb_avalanche/README.md +57 -0
- pantheon_gpu/kernels/tlb_avalanche/tlb_avalanche.cpp +295 -0
- pantheon_gpu/kernels/tlb_avalanche/tlb_avalanche_flow.svg +35 -0
- pantheon_gpu/kernels/transformer_train_step/README.md +3 -0
- pantheon_gpu/kernels/transformer_train_step/transformer_train_step.cpp +2 -0
- pantheon_gpu/kernels/transformer_virus/README.md +43 -0
- pantheon_gpu/kernels/transformer_virus/transformer_virus.cpp +393 -0
- pantheon_gpu/kernels/transformer_virus/transformer_virus_flow.svg +10 -0
- pantheon_gpu/kernels/vision_encoder/README.md +3 -0
- pantheon_gpu/kernels/vision_encoder/vision_encoder.cpp +2 -0
- pantheon_gpu/monitor.py +417 -0
- pantheon_gpu/pantheon.py +2572 -0
- pantheon_gpu-1.2.0.dist-info/METADATA +270 -0
- pantheon_gpu-1.2.0.dist-info/RECORD +137 -0
- pantheon_gpu-1.2.0.dist-info/WHEEL +5 -0
- pantheon_gpu-1.2.0.dist-info/entry_points.txt +3 -0
- pantheon_gpu-1.2.0.dist-info/licenses/LICENSE +201 -0
- pantheon_gpu-1.2.0.dist-info/licenses/NOTICE +42 -0
- pantheon_gpu-1.2.0.dist-info/top_level.txt +1 -0
pantheon_gpu/Makefile
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# PANTHEON Build System
|
|
2
|
+
|
|
3
|
+
# --- Compiler Configuration ---
|
|
4
|
+
HIPCC ?= hipcc
|
|
5
|
+
CXXFLAGS := -O3 -std=c++14 -DNDEBUG -Ikernels/common
|
|
6
|
+
BUILD_DIR := build
|
|
7
|
+
ROCM_PATH ?= /opt/rocm
|
|
8
|
+
BUILD_STAMP := $(BUILD_DIR)/.platform
|
|
9
|
+
|
|
10
|
+
# Platform Detection
|
|
11
|
+
ifeq ($(PLATFORM), MOCK)
|
|
12
|
+
COMPILER := g++
|
|
13
|
+
# Enable Mock Flag and suppress warnings about GPU-specific pragmas
|
|
14
|
+
CXXFLAGS += -DPANTHEON_MOCK -Wno-unknown-pragmas -pthread
|
|
15
|
+
|
|
16
|
+
else ifeq ($(PLATFORM), CUDA)
|
|
17
|
+
COMPILER := nvcc
|
|
18
|
+
|
|
19
|
+
# 1. Detect physical GPU architecture
|
|
20
|
+
# nvidia-smi writes "NVIDIA-SMI has failed because it couldn't communicate
|
|
21
|
+
# with the NVIDIA driver" to STDOUT, so 2>/dev/null does not suppress it.
|
|
22
|
+
# Without the numeric filter that sentence becomes DETECTED_ARCH: it is
|
|
23
|
+
# non-empty, so the failsafe below never fires, and it reaches the compiler
|
|
24
|
+
# as sm_<sentence> -- whose apostrophe breaks shell quoting, producing
|
|
25
|
+
# "/bin/sh: Syntax error: Unterminated quoted string" instead of the
|
|
26
|
+
# readable fallback this block was written to provide.
|
|
27
|
+
DETECTED_ARCH := $(strip $(shell nvidia-smi --query-gpu=compute_cap --format=csv,noheader 2>/dev/null | head -n 1 | tr -d '.' | grep -E '^[0-9]+$$'))
|
|
28
|
+
# Print it immediately during the parsing phase
|
|
29
|
+
$(info [DEBUG] The detected architecture is: $(DETECTED_ARCH))
|
|
30
|
+
|
|
31
|
+
# 2. Hopper WGMMA Promotion
|
|
32
|
+
# Hopper requires the 'a' suffix (sm_90a) to unlock WGMMA PTX instructions
|
|
33
|
+
ifeq ($(DETECTED_ARCH), 90)
|
|
34
|
+
DETECTED_ARCH := 90a
|
|
35
|
+
endif
|
|
36
|
+
|
|
37
|
+
# 3. The Ultimate Failsafe
|
|
38
|
+
# If nvidia-smi fails (e.g., compiling on a CPU-only login node),
|
|
39
|
+
# default to Hopper (90a) so the build doesn't crash with an empty 'sm_' flag.
|
|
40
|
+
ifeq ($(DETECTED_ARCH),)
|
|
41
|
+
$(info [DEBUG] Hardware detection failed. Falling back to default sm_86...)
|
|
42
|
+
DETECTED_ARCH := 86
|
|
43
|
+
endif
|
|
44
|
+
|
|
45
|
+
# --- Vendored Headers ---
|
|
46
|
+
# OptiX headers are NVIDIA-proprietary and are not redistributable, so this
|
|
47
|
+
# tree does not require them. Set OPTIX_PATH to an OptiX SDK include
|
|
48
|
+
# directory to build the real RT kernel; otherwise rt_virus builds its dummy
|
|
49
|
+
# kernel, exactly as it already does when HIP-RT is missing on AMD.
|
|
50
|
+
OPTIX_PATH ?= $(wildcard kernels/common/optix)
|
|
51
|
+
ifneq ($(OPTIX_PATH),)
|
|
52
|
+
OPTIX_INC := -I $(OPTIX_PATH)
|
|
53
|
+
else
|
|
54
|
+
OPTIX_INC :=
|
|
55
|
+
$(info [DEBUG] OptiX headers not found; rt_virus will build its dummy kernel. Set OPTIX_PATH to enable it.)
|
|
56
|
+
endif
|
|
57
|
+
NVENC_INC := -I kernels/common/nvenc
|
|
58
|
+
|
|
59
|
+
CXXFLAGS += -x cu --gpu-architecture=sm_$(DETECTED_ARCH) -Wno-deprecated-gpu-targets $(OPTIX_INC) $(NVENC_INC)
|
|
60
|
+
|
|
61
|
+
# --- Link the CUDA Driver API and Dynamic Loader ---
|
|
62
|
+
LDFLAGS += -lcuda -ldl
|
|
63
|
+
|
|
64
|
+
else
|
|
65
|
+
COMPILER := $(HIPCC)
|
|
66
|
+
|
|
67
|
+
# 1. Detect physical GPU architecture (Modern ROCm uses amdgpu-arch)
|
|
68
|
+
DETECTED_GFX := $(strip $(shell amdgpu-arch 2>/dev/null | head -n 1))
|
|
69
|
+
ifeq ($(DETECTED_GFX),)
|
|
70
|
+
DETECTED_GFX := $(strip $(shell rocm_agent_enumerator 2>/dev/null | grep -v "cpu" | head -n 1))
|
|
71
|
+
endif
|
|
72
|
+
|
|
73
|
+
# 2. Manual Override & Failsafe
|
|
74
|
+
# Allows cross-compiling on CPU nodes via: make PLATFORM=HIP TARGET_GFX=gfx950
|
|
75
|
+
ifdef TARGET_GFX
|
|
76
|
+
DETECTED_GFX := $(TARGET_GFX)
|
|
77
|
+
endif
|
|
78
|
+
|
|
79
|
+
# Tool output can include feature suffixes such as gfx90a:sramecc+:xnack-.
|
|
80
|
+
# hipcc's --offload-arch expects the architecture name only.
|
|
81
|
+
DETECTED_GFX := $(firstword $(subst :, ,$(DETECTED_GFX)))
|
|
82
|
+
|
|
83
|
+
ifeq ($(DETECTED_GFX),)
|
|
84
|
+
$(info [DEBUG] Hardware detection failed. Falling back to MI300X default...)
|
|
85
|
+
DETECTED_GFX := gfx942
|
|
86
|
+
endif
|
|
87
|
+
|
|
88
|
+
$(info [DEBUG] The AMD target architecture is: $(DETECTED_GFX))
|
|
89
|
+
|
|
90
|
+
# RDNA-family parts (gfx11xx, gfx12xx) provide WMMA; CDNA Instinct parts
|
|
91
|
+
# provide MFMA. Match on the two-digit family prefix rather than on
|
|
92
|
+
# individual model numbers, so any member of a family -- including ones
|
|
93
|
+
# that do not exist yet -- selects the right path with no source change.
|
|
94
|
+
GFX_FAMILY := $(shell printf '%s' '$(DETECTED_GFX)' | sed -E 's/^(gfx[0-9]{2}).*/\1/')
|
|
95
|
+
ifneq ($(filter gfx11 gfx12,$(GFX_FAMILY)),)
|
|
96
|
+
CXXFLAGS += -DPANTHEON_AMD_WMMA_TARGET=1
|
|
97
|
+
$(info [DEBUG] WMMA-capable family detected ($(GFX_FAMILY)); enabling WMMA path.)
|
|
98
|
+
endif
|
|
99
|
+
|
|
100
|
+
# 3. FIX: Force Native Compilation for Pantheon Kernels
|
|
101
|
+
# If a specific cross-compile target wasn't requested, trust the compiler to
|
|
102
|
+
# generate machine code for the exact silicon it is currently sitting on.
|
|
103
|
+
# This completely prevents Code 98 on bleeding-edge chips like the MI350X.
|
|
104
|
+
ifdef TARGET_GFX
|
|
105
|
+
CXXFLAGS += -std=c++17 --offload-arch=$(DETECTED_GFX)
|
|
106
|
+
else
|
|
107
|
+
CXXFLAGS += -std=c++17 --offload-arch=native
|
|
108
|
+
endif
|
|
109
|
+
|
|
110
|
+
# --- Optional Local HIP-RT Vendoring ---
|
|
111
|
+
# The RT kernel builds a dummy fallback when HIP-RT headers are absent, so
|
|
112
|
+
# do not make every AMD build clone and compile HIP-RT by default.
|
|
113
|
+
VENDOR_DIR := vendor
|
|
114
|
+
HIPRT_DIR := $(VENDOR_DIR)/HIPRT
|
|
115
|
+
HIPRT_INSTALL := $(VENDOR_DIR)/install
|
|
116
|
+
HIPRT_TARGET := $(HIPRT_INSTALL)/lib/libhiprt64.so
|
|
117
|
+
|
|
118
|
+
ifeq ($(ENABLE_HIPRT),1)
|
|
119
|
+
CXXFLAGS += -I$(abspath $(HIPRT_INSTALL)/include)
|
|
120
|
+
LDFLAGS += -L$(abspath $(HIPRT_INSTALL)/lib) -lhiprt64 -Wl,-rpath=$(abspath $(HIPRT_INSTALL)/lib)
|
|
121
|
+
PLATFORM_DEPS := $(HIPRT_TARGET)
|
|
122
|
+
endif
|
|
123
|
+
endif
|
|
124
|
+
|
|
125
|
+
# OPTIX_PATH is part of the signature: setting it after a build without it
|
|
126
|
+
# must invalidate the cached binaries, or rt_virus silently keeps running the
|
|
127
|
+
# dummy kernel it compiled when the headers were absent.
|
|
128
|
+
BUILD_SIGNATURE := PLATFORM=$(PLATFORM);CUDA_ARCH=$(DETECTED_ARCH);AMD_GFX=$(DETECTED_GFX);TARGET_GFX=$(DETECTED_GFX);ENABLE_HIPRT=$(ENABLE_HIPRT);OPTIX=$(OPTIX_PATH)
|
|
129
|
+
|
|
130
|
+
# --- Auto-Discovery Logic ---
|
|
131
|
+
|
|
132
|
+
# 1. Find all .cpp files inside kernels/ subdirectories
|
|
133
|
+
ALL_SRCS := $(shell find kernels -name "*.cpp")
|
|
134
|
+
|
|
135
|
+
# 2. Exclude anything in the 'common' directory or root headers
|
|
136
|
+
SRCS := $(filter-out kernels/common/%, $(ALL_SRCS))
|
|
137
|
+
|
|
138
|
+
# Empty discovery means find is unavailable (minimal container images such as
|
|
139
|
+
# rockylinux:8 omit findutils) or the kernels tree is missing. Without this
|
|
140
|
+
# guard make exits 0 having built nothing and every workload fails at launch.
|
|
141
|
+
ifeq ($(strip $(SRCS)),)
|
|
142
|
+
$(error No kernel sources found under kernels/. Verify that 'find' is installed and the kernels/ directory exists)
|
|
143
|
+
endif
|
|
144
|
+
|
|
145
|
+
# 3. Determine Target Binaries
|
|
146
|
+
BINS := $(foreach src,$(SRCS),$(BUILD_DIR)/$(basename $(notdir $(src))))
|
|
147
|
+
|
|
148
|
+
# Shared headers included by many kernel translation units.
|
|
149
|
+
COMMON_HEADERS := kernels/common/common.h kernels/common/ai_workload_template.h
|
|
150
|
+
|
|
151
|
+
# --- Targets ---
|
|
152
|
+
|
|
153
|
+
.PHONY: all clean directories FORCE
|
|
154
|
+
|
|
155
|
+
# Add PLATFORM_DEPS here so HIP-RT is built before the binaries
|
|
156
|
+
all: directories $(BUILD_STAMP) $(PLATFORM_DEPS) $(BINS)
|
|
157
|
+
|
|
158
|
+
$(BUILD_STAMP): FORCE | directories
|
|
159
|
+
@if [ ! -f "$@" ] || [ "$$(cat "$@")" != "$(BUILD_SIGNATURE)" ]; then \
|
|
160
|
+
echo "[BUILD] Platform signature changed. Clearing stale binaries..."; \
|
|
161
|
+
find $(BUILD_DIR) -maxdepth 1 -type f ! -name ".platform" -delete; \
|
|
162
|
+
printf '%s\n' '$(BUILD_SIGNATURE)' > "$@"; \
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
$(BINS): $(BUILD_STAMP)
|
|
166
|
+
|
|
167
|
+
# --- Auto-Dependency: HIP-RT ---
|
|
168
|
+
$(HIPRT_TARGET):
|
|
169
|
+
@echo "============================================================"
|
|
170
|
+
@echo "[DEPENDENCY] Fetching and building HIP-RT locally..."
|
|
171
|
+
@echo "============================================================"
|
|
172
|
+
@mkdir -p $(VENDOR_DIR)
|
|
173
|
+
@if [ ! -d "$(HIPRT_DIR)" ]; then \
|
|
174
|
+
git clone https://github.com/GPUOpen-LibrariesAndSDKs/HIPRT.git $(HIPRT_DIR); \
|
|
175
|
+
cd $(HIPRT_DIR) && git submodule update --init --recursive; \
|
|
176
|
+
fi
|
|
177
|
+
@echo "[DEPENDENCY] Injecting $(DETECTED_GFX) architecture into AMD build scripts..."
|
|
178
|
+
@find $(HIPRT_DIR) -type f \( -name "*.py" -o -name "CMakeLists.txt" \) -exec sed -i "s/'gfx942'/'gfx942', '$(DETECTED_GFX)'/g" {} +
|
|
179
|
+
@find $(HIPRT_DIR) -type f \( -name "*.py" -o -name "CMakeLists.txt" \) -exec sed -i 's/"gfx942"/"gfx942", "$(DETECTED_GFX)"/g' {} +
|
|
180
|
+
@find $(HIPRT_DIR) -type f -name "CMakeLists.txt" -exec sed -i 's/gfx942/gfx942;$(DETECTED_GFX)/g' {} +
|
|
181
|
+
@mkdir -p $(HIPRT_DIR)/build
|
|
182
|
+
@cd $(HIPRT_DIR)/build && cmake -DCMAKE_BUILD_TYPE=Release -DBITCODE=ON -DPRECOMPILE=ON -DFORCE_DISABLE_CUDA=ON -DHIP_PATH=$(ROCM_PATH) -DPYTHON_EXECUTABLE=/usr/bin/python3 -DAMDGPU_TARGETS=$(DETECTED_GFX) -DGPU_TARGETS=$(DETECTED_GFX) ..
|
|
183
|
+
@cd $(HIPRT_DIR)/build && cmake --build . --config Release -j $$(nproc)
|
|
184
|
+
@echo "[DEPENDENCY] Build finished. Extracting all runtime assets..."
|
|
185
|
+
@mkdir -p $(HIPRT_INSTALL)/lib
|
|
186
|
+
@mkdir -p $(HIPRT_INSTALL)/include
|
|
187
|
+
@find $(HIPRT_DIR) -type f \( -name "*.hipfb" -o -name "*.bc" -o -name "libhiprt*64.so*" \) -exec cp -L {} $(HIPRT_INSTALL)/lib/ \;
|
|
188
|
+
@REAL_SO=$$(find $(HIPRT_INSTALL)/lib -name "libhiprt*64.so*" -type f | head -n 1); \
|
|
189
|
+
ln -sf $$(basename $$REAL_SO) $(HIPRT_INSTALL)/lib/libhiprt64.so
|
|
190
|
+
@cp -r $(HIPRT_DIR)/hiprt $(HIPRT_INSTALL)/include/
|
|
191
|
+
@echo "[DEPENDENCY] HIP-RT and Orochi assets successfully installed to local vendor folder!"
|
|
192
|
+
|
|
193
|
+
# Create build directory
|
|
194
|
+
directories:
|
|
195
|
+
@mkdir -p $(BUILD_DIR)
|
|
196
|
+
|
|
197
|
+
# Generic Rule: Build any binary from its corresponding source found in kernels tree
|
|
198
|
+
vpath %.cpp $(sort $(dir $(SRCS)))
|
|
199
|
+
|
|
200
|
+
$(BUILD_DIR)/%: %.cpp $(COMMON_HEADERS) $(BUILD_STAMP)
|
|
201
|
+
@echo "[BUILD] Compiling $< -> $@"
|
|
202
|
+
@$(COMPILER) $(CXXFLAGS) $< -o $@ $(LDFLAGS)
|
|
203
|
+
|
|
204
|
+
clean:
|
|
205
|
+
@echo "[CLEAN] Removing build artifacts..."
|
|
206
|
+
@rm -rf $(BUILD_DIR)
|
|
207
|
+
@rm -rf $(VENDOR_DIR)
|
pantheon_gpu/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.2.0
|
pantheon_gpu/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Pantheon GPU stress and diagnostics suite."""
|
pantheon_gpu/_entry.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Console entry point for an installed Pantheon."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def main():
|
|
7
|
+
# site-packages is not writable, and the runner defaults to building
|
|
8
|
+
# beside its own source unless told otherwise. Respect an explicit
|
|
9
|
+
# override so a user can still choose the location.
|
|
10
|
+
if not os.environ.get("PANTHEON_BUILD_CACHE_DIR", "").strip():
|
|
11
|
+
cache = os.environ.get("XDG_CACHE_HOME", "").strip() or os.path.join(
|
|
12
|
+
os.path.expanduser("~"), ".cache")
|
|
13
|
+
os.environ["PANTHEON_BUILD_CACHE_DIR"] = os.path.join(
|
|
14
|
+
cache, "pantheongpu", "builds")
|
|
15
|
+
|
|
16
|
+
from . import pantheon
|
|
17
|
+
return pantheon.main()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# All-Reduce
|
|
2
|
+
|
|
3
|
+
`all_reduce` is a two-rank GPU collective. It initializes one rank with `1`
|
|
4
|
+
and the other with `2`, sums them on the primary GPU, then broadcasts the
|
|
5
|
+
resulting `3` back to both ranks. `--verify` checks both GPU buffers.
|
|
6
|
+
|
|
7
|
+
Pantheon uses direct peer DMA when bidirectional peer access is available. If
|
|
8
|
+
the topology does not expose peer access, it reports a host-staged fallback
|
|
9
|
+
instead. The fallback validates correctness but should not be used as a direct
|
|
10
|
+
P2P bandwidth measurement.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pantheon --test all_reduce --duration 60 --gpu 0 --verify --profile
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Use `--inject_error --verify` to confirm that verification detects a corrupted
|
|
17
|
+
value on the peer rank.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#include "../common/common.h"
|
|
2
|
+
|
|
3
|
+
#include <chrono>
|
|
4
|
+
#include <iostream>
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
// A real two-rank all-reduce. GPU 1's input is copied to GPU 0, reduced
|
|
8
|
+
// there, then the completed value is broadcast back to GPU 1.
|
|
9
|
+
__global__ void init_collective(unsigned int* data, size_t count, unsigned int value) {
|
|
10
|
+
size_t idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
11
|
+
size_t stride = blockDim.x * gridDim.x;
|
|
12
|
+
for (; idx < count; idx += stride) data[idx] = value;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
__global__ void add_collective(unsigned int* dst, const unsigned int* src, size_t count) {
|
|
16
|
+
size_t idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
17
|
+
size_t stride = blockDim.x * gridDim.x;
|
|
18
|
+
for (; idx < count; idx += stride) dst[idx] += src[idx];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
__global__ void corrupt_collective(unsigned int* data) {
|
|
22
|
+
if (blockIdx.x == 0 && threadIdx.x == 0) data[0] ^= 1u;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
__global__ void verify_collective(const unsigned int* data, size_t count, unsigned int expected, unsigned int* errors) {
|
|
26
|
+
size_t idx = blockIdx.x * blockDim.x + threadIdx.x;
|
|
27
|
+
size_t stride = blockDim.x * gridDim.x;
|
|
28
|
+
for (; idx < count; idx += stride) if (data[idx] != expected) atomicAdd(errors, 1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
int main(int argc, char** argv) {
|
|
32
|
+
if (argc < 4) return 1;
|
|
33
|
+
int primary = atoi(argv[1]);
|
|
34
|
+
int duration = atoi(argv[2]);
|
|
35
|
+
bool verify = false;
|
|
36
|
+
bool inject_error = false;
|
|
37
|
+
for (int i = 1; i < argc; ++i) {
|
|
38
|
+
std::string arg(argv[i]);
|
|
39
|
+
if (arg == "--verify") verify = true;
|
|
40
|
+
else if (arg == "--inject_error") inject_error = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
int devices = 0;
|
|
44
|
+
CHECK(hipGetDeviceCount(&devices));
|
|
45
|
+
if (devices < 2) {
|
|
46
|
+
std::cout << "[PANTHEON] Skipping ALL_REDUCE: fewer than two GPUs." << std::endl;
|
|
47
|
+
std::cout << "Throughput: 0.0 GB/s" << std::endl;
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
int peer = (primary + 1) % devices;
|
|
52
|
+
int forward_peer_access = 0;
|
|
53
|
+
int reverse_peer_access = 0;
|
|
54
|
+
CHECK(hipDeviceCanAccessPeer(&forward_peer_access, primary, peer));
|
|
55
|
+
CHECK(hipDeviceCanAccessPeer(&reverse_peer_access, peer, primary));
|
|
56
|
+
bool p2p = forward_peer_access && reverse_peer_access;
|
|
57
|
+
if (p2p) {
|
|
58
|
+
CHECK(hipSetDevice(primary));
|
|
59
|
+
CHECK(hipDeviceEnablePeerAccess(peer, 0));
|
|
60
|
+
CHECK(hipSetDevice(peer));
|
|
61
|
+
CHECK(hipDeviceEnablePeerAccess(primary, 0));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const size_t bytes = 64ull * 1024 * 1024;
|
|
65
|
+
const size_t count = bytes / sizeof(unsigned int);
|
|
66
|
+
const int blocks = 4096;
|
|
67
|
+
unsigned int* primary_data = nullptr;
|
|
68
|
+
unsigned int* peer_data = nullptr;
|
|
69
|
+
unsigned int* scratch = nullptr;
|
|
70
|
+
void* host_stage = nullptr;
|
|
71
|
+
|
|
72
|
+
CHECK(hipSetDevice(primary));
|
|
73
|
+
CHECK(hipMalloc(&primary_data, bytes));
|
|
74
|
+
CHECK(hipMalloc(&scratch, bytes));
|
|
75
|
+
if (!p2p) CHECK(hipHostMalloc(&host_stage, bytes));
|
|
76
|
+
CHECK(hipSetDevice(peer));
|
|
77
|
+
CHECK(hipMalloc(&peer_data, bytes));
|
|
78
|
+
CHECK(hipSetDevice(primary));
|
|
79
|
+
hipStream_t stream;
|
|
80
|
+
CHECK(hipStreamCreate(&stream));
|
|
81
|
+
|
|
82
|
+
LAUNCH_KERNEL(init_collective, blocks, 256, primary_data, count, 1u);
|
|
83
|
+
CHECK(hipSetDevice(peer));
|
|
84
|
+
LAUNCH_KERNEL(init_collective, blocks, 256, peer_data, count, 2u);
|
|
85
|
+
CHECK(hipDeviceSynchronize());
|
|
86
|
+
CHECK(hipSetDevice(primary));
|
|
87
|
+
|
|
88
|
+
auto reduce_and_broadcast = [&]() {
|
|
89
|
+
if (p2p) {
|
|
90
|
+
CHECK(hipMemcpyPeerAsync(scratch, primary, peer_data, peer, bytes, stream));
|
|
91
|
+
} else {
|
|
92
|
+
CHECK(hipSetDevice(peer));
|
|
93
|
+
CHECK(hipMemcpy(host_stage, peer_data, bytes, hipMemcpyDeviceToHost));
|
|
94
|
+
CHECK(hipSetDevice(primary));
|
|
95
|
+
CHECK(hipMemcpy(scratch, host_stage, bytes, hipMemcpyHostToDevice));
|
|
96
|
+
}
|
|
97
|
+
LAUNCH_KERNEL(add_collective, blocks, 256, primary_data, scratch, count);
|
|
98
|
+
if (p2p) {
|
|
99
|
+
CHECK(hipMemcpyPeerAsync(peer_data, peer, primary_data, primary, bytes, stream));
|
|
100
|
+
CHECK(hipStreamSynchronize(stream));
|
|
101
|
+
} else {
|
|
102
|
+
CHECK(hipMemcpy(host_stage, primary_data, bytes, hipMemcpyDeviceToHost));
|
|
103
|
+
CHECK(hipSetDevice(peer));
|
|
104
|
+
CHECK(hipMemcpy(peer_data, host_stage, bytes, hipMemcpyHostToDevice));
|
|
105
|
+
CHECK(hipDeviceSynchronize());
|
|
106
|
+
CHECK(hipSetDevice(primary));
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
std::cout << "[PANTHEON] GPU " << primary << ": ALL_REDUCE with GPU " << peer
|
|
111
|
+
<< (p2p ? " (peer DMA)" : " (host-staged fallback)") << std::endl;
|
|
112
|
+
auto start = std::chrono::high_resolution_clock::now();
|
|
113
|
+
// Time only the collective itself: the between-iteration re-initialization
|
|
114
|
+
// is bookkeeping, not interconnect traffic, and must not dilute GB/s.
|
|
115
|
+
double reduce_seconds = 0.0;
|
|
116
|
+
size_t transferred = 0;
|
|
117
|
+
do {
|
|
118
|
+
auto step_start = std::chrono::high_resolution_clock::now();
|
|
119
|
+
reduce_and_broadcast();
|
|
120
|
+
reduce_seconds += std::chrono::duration<double>(
|
|
121
|
+
std::chrono::high_resolution_clock::now() - step_start).count();
|
|
122
|
+
LAUNCH_KERNEL(init_collective, blocks, 256, primary_data, count, 1u);
|
|
123
|
+
CHECK(hipSetDevice(peer));
|
|
124
|
+
LAUNCH_KERNEL(init_collective, blocks, 256, peer_data, count, 2u);
|
|
125
|
+
CHECK(hipDeviceSynchronize());
|
|
126
|
+
CHECK(hipSetDevice(primary));
|
|
127
|
+
// One gather crossing plus one broadcast crossing per collective.
|
|
128
|
+
transferred += bytes * 2;
|
|
129
|
+
} while (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::high_resolution_clock::now() - start).count() < duration);
|
|
130
|
+
double seconds = reduce_seconds > 0.0 ? reduce_seconds : 1e-6;
|
|
131
|
+
std::cout << "Throughput: " << (transferred / 1e9) / seconds << " GB/s" << std::endl;
|
|
132
|
+
|
|
133
|
+
int exit_code = 0;
|
|
134
|
+
if (verify) {
|
|
135
|
+
reduce_and_broadcast();
|
|
136
|
+
if (inject_error) {
|
|
137
|
+
CHECK(hipSetDevice(peer));
|
|
138
|
+
LAUNCH_KERNEL(corrupt_collective, 1, 1, peer_data);
|
|
139
|
+
CHECK(hipDeviceSynchronize());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
unsigned int* primary_errors = nullptr;
|
|
143
|
+
unsigned int* peer_errors = nullptr;
|
|
144
|
+
unsigned int primary_error_count = 0;
|
|
145
|
+
unsigned int peer_error_count = 0;
|
|
146
|
+
CHECK(hipSetDevice(primary));
|
|
147
|
+
CHECK(hipMalloc(&primary_errors, sizeof(unsigned int)));
|
|
148
|
+
CHECK(hipMemset(primary_errors, 0, sizeof(unsigned int)));
|
|
149
|
+
LAUNCH_KERNEL(verify_collective, blocks, 256, primary_data, count, 3u, primary_errors);
|
|
150
|
+
CHECK(hipDeviceSynchronize());
|
|
151
|
+
CHECK(hipMemcpy(&primary_error_count, primary_errors, sizeof(primary_error_count), hipMemcpyDeviceToHost));
|
|
152
|
+
CHECK(hipFree(primary_errors));
|
|
153
|
+
|
|
154
|
+
CHECK(hipSetDevice(peer));
|
|
155
|
+
CHECK(hipMalloc(&peer_errors, sizeof(unsigned int)));
|
|
156
|
+
CHECK(hipMemset(peer_errors, 0, sizeof(unsigned int)));
|
|
157
|
+
LAUNCH_KERNEL(verify_collective, blocks, 256, peer_data, count, 3u, peer_errors);
|
|
158
|
+
CHECK(hipDeviceSynchronize());
|
|
159
|
+
CHECK(hipMemcpy(&peer_error_count, peer_errors, sizeof(peer_error_count), hipMemcpyDeviceToHost));
|
|
160
|
+
CHECK(hipFree(peer_errors));
|
|
161
|
+
unsigned int errors = primary_error_count + peer_error_count;
|
|
162
|
+
if (errors) {
|
|
163
|
+
std::cout << "Verification: FAIL (" << errors << " errors)" << std::endl;
|
|
164
|
+
exit_code = 1;
|
|
165
|
+
} else {
|
|
166
|
+
std::cout << "Verification: PASS" << std::endl;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
CHECK(hipSetDevice(primary));
|
|
171
|
+
CHECK(hipStreamDestroy(stream));
|
|
172
|
+
CHECK(hipFree(primary_data));
|
|
173
|
+
CHECK(hipFree(scratch));
|
|
174
|
+
if (host_stage) CHECK(hipHostFree(host_stage));
|
|
175
|
+
CHECK(hipSetDevice(peer));
|
|
176
|
+
CHECK(hipFree(peer_data));
|
|
177
|
+
return exit_code;
|
|
178
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Allocation Fragmentation
|
|
2
|
+
//
|
|
3
|
+
// This workload previously included the shared AI kernel template and performed
|
|
4
|
+
// no allocation at all -- it ran the same FMA loop as five other workloads while
|
|
5
|
+
// its name promised allocator behaviour. It now does what it says: repeatedly
|
|
6
|
+
// allocates and frees device memory in a pattern that fragments the heap, and
|
|
7
|
+
// measures how the allocator copes.
|
|
8
|
+
//
|
|
9
|
+
// The stress is on the driver's allocator, not on the GPU pipelines, so the
|
|
10
|
+
// device stays almost idle here by design.
|
|
11
|
+
|
|
12
|
+
#include "../common/common.h"
|
|
13
|
+
|
|
14
|
+
#include <algorithm>
|
|
15
|
+
#include <chrono>
|
|
16
|
+
#include <cstdio>
|
|
17
|
+
#include <cstdlib>
|
|
18
|
+
#include <iostream>
|
|
19
|
+
#include <string>
|
|
20
|
+
#include <vector>
|
|
21
|
+
|
|
22
|
+
// Touch each block so the allocation is actually backed, and so a handed-back
|
|
23
|
+
// pointer that overlaps a live one shows up as corruption rather than passing
|
|
24
|
+
// silently.
|
|
25
|
+
__global__ void stamp_block(unsigned int* block, size_t words, unsigned int tag) {
|
|
26
|
+
size_t stride = blockDim.x * gridDim.x;
|
|
27
|
+
for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < words; i += stride) {
|
|
28
|
+
block[i] = tag ^ static_cast<unsigned int>(i);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
__global__ void check_block(const unsigned int* block, size_t words, unsigned int tag,
|
|
33
|
+
unsigned int* errors) {
|
|
34
|
+
size_t stride = blockDim.x * gridDim.x;
|
|
35
|
+
for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < words; i += stride) {
|
|
36
|
+
if (block[i] != (tag ^ static_cast<unsigned int>(i))) atomicAdd(errors, 1u);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
int main(int argc, char* argv[]) {
|
|
41
|
+
if (argc < 4) return 1;
|
|
42
|
+
int gpu_id = atoi(argv[1]);
|
|
43
|
+
int duration = atoi(argv[2]);
|
|
44
|
+
int mem_pct = atoi(argv[3]);
|
|
45
|
+
|
|
46
|
+
bool verify_mode = false;
|
|
47
|
+
int inject_error = 0;
|
|
48
|
+
int block_size = 256, grid_size = 0, kernel_loops = 1, warmup_iters = 0, sync_mode = 2;
|
|
49
|
+
for (int i = 4; i < argc; ++i) {
|
|
50
|
+
std::string a(argv[i]);
|
|
51
|
+
if (a == "--verify") verify_mode = true;
|
|
52
|
+
else if (a == "--inject_error") inject_error = 1;
|
|
53
|
+
else if (a == "--block_size" && i + 1 < argc) block_size = atoi(argv[++i]);
|
|
54
|
+
else if (a == "--grid_size" && i + 1 < argc) grid_size = atoi(argv[++i]);
|
|
55
|
+
else if (a == "--warmup_iters" && i + 1 < argc) warmup_iters = atoi(argv[++i]);
|
|
56
|
+
else if (a == "--sync_mode" && i + 1 < argc) sync_mode = atoi(argv[++i]);
|
|
57
|
+
}
|
|
58
|
+
normalize_kernel_launch_config(block_size, grid_size, kernel_loops, warmup_iters, sync_mode);
|
|
59
|
+
|
|
60
|
+
CHECK(hipSetDevice(gpu_id));
|
|
61
|
+
hipDeviceProp_t prop;
|
|
62
|
+
CHECK(hipGetDeviceProperties(&prop, gpu_id));
|
|
63
|
+
|
|
64
|
+
size_t free_bytes = 0, total_bytes = 0;
|
|
65
|
+
CHECK(hipMemGetInfo(&free_bytes, &total_bytes));
|
|
66
|
+
mem_pct = mem_pct < 1 ? 1 : (mem_pct > 99 ? 99 : mem_pct);
|
|
67
|
+
size_t budget = (free_bytes * static_cast<size_t>(mem_pct)) / 100;
|
|
68
|
+
|
|
69
|
+
// A spread of sizes is what fragments a heap. Equal-sized blocks are always
|
|
70
|
+
// reusable and would never expose the behaviour this test is named for.
|
|
71
|
+
const size_t kSmall = 64u * 1024u;
|
|
72
|
+
const size_t kLarge = 8u * 1024u * 1024u;
|
|
73
|
+
const size_t kSlots = 512;
|
|
74
|
+
|
|
75
|
+
std::cout << "[PANTHEON] GPU " << gpu_id << ": Allocation Fragmentation" << std::endl;
|
|
76
|
+
std::cout << " -> Budget: " << (budget / (1024 * 1024)) << " MiB" << std::endl;
|
|
77
|
+
std::cout << " -> Block sizes: " << (kSmall / 1024) << " KiB to "
|
|
78
|
+
<< (kLarge / (1024 * 1024)) << " MiB" << std::endl;
|
|
79
|
+
std::cout << " -> Slots: " << kSlots << std::endl;
|
|
80
|
+
std::cout << " -> Verify Mode: " << (verify_mode ? "ON" : "OFF") << std::endl;
|
|
81
|
+
if (inject_error) std::cout << "[PANTHEON] Warning: SDC Fault Injection is ACTIVE!" << std::endl;
|
|
82
|
+
|
|
83
|
+
std::vector<unsigned int*> slots(kSlots, nullptr);
|
|
84
|
+
std::vector<size_t> sizes(kSlots, 0);
|
|
85
|
+
std::vector<unsigned int> tags(kSlots, 0);
|
|
86
|
+
size_t live_bytes = 0;
|
|
87
|
+
unsigned long long allocations = 0, frees = 0, failures = 0;
|
|
88
|
+
unsigned int* d_errors = nullptr;
|
|
89
|
+
if (verify_mode) {
|
|
90
|
+
CHECK(hipMalloc(&d_errors, sizeof(unsigned int)));
|
|
91
|
+
CHECK(hipMemset(d_errors, 0, sizeof(unsigned int)));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
unsigned int rng = 0x9E3779B9u;
|
|
95
|
+
auto next = [&rng]() { rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5; return rng; };
|
|
96
|
+
|
|
97
|
+
std::cout << "[PANTHEON] Starting active telemetry phase..." << std::endl;
|
|
98
|
+
auto start = std::chrono::high_resolution_clock::now();
|
|
99
|
+
do {
|
|
100
|
+
for (size_t round = 0; round < kSlots; ++round) {
|
|
101
|
+
size_t slot = next() % kSlots;
|
|
102
|
+
|
|
103
|
+
if (slots[slot]) {
|
|
104
|
+
// Free roughly half the live blocks each pass, leaving holes of
|
|
105
|
+
// mixed sizes behind rather than a clean sweep.
|
|
106
|
+
CHECK(hipFree(slots[slot]));
|
|
107
|
+
live_bytes -= sizes[slot];
|
|
108
|
+
slots[slot] = nullptr;
|
|
109
|
+
sizes[slot] = 0;
|
|
110
|
+
++frees;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
size_t want = kSmall + (static_cast<size_t>(next()) % (kLarge - kSmall));
|
|
115
|
+
want &= ~static_cast<size_t>(255);
|
|
116
|
+
if (live_bytes + want > budget) continue;
|
|
117
|
+
|
|
118
|
+
unsigned int* block = nullptr;
|
|
119
|
+
if (hipMalloc(&block, want) != hipSuccess || block == nullptr) {
|
|
120
|
+
// A refusal while well under budget is the fragmentation signal.
|
|
121
|
+
++failures;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
size_t words = want / sizeof(unsigned int);
|
|
125
|
+
unsigned int tag = next();
|
|
126
|
+
int grid = init_launch_grid_size(prop, words, block_size);
|
|
127
|
+
LAUNCH_KERNEL(stamp_block, grid, block_size, block, words, tag);
|
|
128
|
+
slots[slot] = block;
|
|
129
|
+
sizes[slot] = want;
|
|
130
|
+
tags[slot] = tag;
|
|
131
|
+
live_bytes += want;
|
|
132
|
+
++allocations;
|
|
133
|
+
}
|
|
134
|
+
CHECK(hipDeviceSynchronize());
|
|
135
|
+
} while (std::chrono::duration_cast<std::chrono::seconds>(
|
|
136
|
+
std::chrono::high_resolution_clock::now() - start).count() < duration);
|
|
137
|
+
|
|
138
|
+
double seconds = std::chrono::duration<double>(
|
|
139
|
+
std::chrono::high_resolution_clock::now() - start).count();
|
|
140
|
+
|
|
141
|
+
std::cout << "Throughput: " << (static_cast<double>(allocations + frees) / seconds)
|
|
142
|
+
<< " alloc-events/s" << std::endl;
|
|
143
|
+
std::cout << " -> Allocations: " << allocations << std::endl;
|
|
144
|
+
std::cout << " -> Frees: " << frees << std::endl;
|
|
145
|
+
std::cout << " -> Refusals: " << failures << " (allocator declined while under budget)" << std::endl;
|
|
146
|
+
std::cout << " -> Live at end: " << (live_bytes / (1024 * 1024)) << " MiB" << std::endl;
|
|
147
|
+
|
|
148
|
+
int exit_code = 0;
|
|
149
|
+
if (verify_mode) {
|
|
150
|
+
std::cout << "[PANTHEON] Verifying every live block still holds its tag..." << std::endl;
|
|
151
|
+
if (inject_error) {
|
|
152
|
+
for (size_t s = 0; s < kSlots; ++s) {
|
|
153
|
+
if (slots[s]) {
|
|
154
|
+
unsigned int bad = 0x0BADBEEFu;
|
|
155
|
+
CHECK(hipMemcpy(slots[s], &bad, sizeof(bad), hipMemcpyHostToDevice));
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
for (size_t s = 0; s < kSlots; ++s) {
|
|
161
|
+
if (!slots[s]) continue;
|
|
162
|
+
size_t words = sizes[s] / sizeof(unsigned int);
|
|
163
|
+
int grid = init_launch_grid_size(prop, words, block_size);
|
|
164
|
+
LAUNCH_KERNEL(check_block, grid, block_size, slots[s], words, tags[s], d_errors);
|
|
165
|
+
}
|
|
166
|
+
CHECK(hipDeviceSynchronize());
|
|
167
|
+
unsigned int host_errors = 0;
|
|
168
|
+
CHECK(hipMemcpy(&host_errors, d_errors, sizeof(unsigned int), hipMemcpyDeviceToHost));
|
|
169
|
+
std::cout << "Verification: " << (host_errors ? "FAIL" : "PASS")
|
|
170
|
+
<< " (" << host_errors << " corrupted words across live blocks)" << std::endl;
|
|
171
|
+
exit_code = host_errors ? 1 : 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
for (size_t s = 0; s < kSlots; ++s) if (slots[s]) CHECK(hipFree(slots[s]));
|
|
175
|
+
if (d_errors) CHECK(hipFree(d_errors));
|
|
176
|
+
return exit_code;
|
|
177
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Atomic Virus
|
|
2
|
+
|
|
3
|
+
`atomic_virus` is an L2/cache-arbiter stress test. It repeatedly performs atomic adds across a large VRAM allocation, forcing read-modify-write traffic that is harder on cache arbitration and memory ordering than plain reads or writes.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
## What It Stresses
|
|
8
|
+
|
|
9
|
+
| Area | Stress mechanism |
|
|
10
|
+
| :--- | :--- |
|
|
11
|
+
| L2 cache arbiters | Many threads contend on atomic read-modify-write paths. |
|
|
12
|
+
| Memory fabric | Wide grid-stride access walks a large allocation. |
|
|
13
|
+
| Ordering hardware | Atomic operations force serialization at cache-line granularity. |
|
|
14
|
+
| Retention/correctness | Optional verification checks the expected atomic count. |
|
|
15
|
+
|
|
16
|
+
## How It Works
|
|
17
|
+
|
|
18
|
+
1. The test uses `--mem` percent of available VRAM to size the buffer, but **the implementation clamps the allocation** (currently 128 MiB max) so each kernel launch finishes in bounded time. On very large GPUs, `--mem 99` would otherwise imply trillions of atomics **per launch** before the next `hipDeviceSynchronize`, and the host watchdog (`duration + 30` s in `pantheon.py`) would SIGKILL the process with exit code **-9** while telemetry still shows `0.0 ERR`. For a true “almost full VRAM” allocation check, use a different test; keep `atomic_virus` for L2/atomic stress at the clamped footprint.
|
|
19
|
+
2. Memory is initialized to zeroes or ones.
|
|
20
|
+
3. Each thread walks the allocation as `uint4` storage reinterpreted as `unsigned int`.
|
|
21
|
+
4. For each visited integer, the kernel performs `kernel_loops` atomic adds.
|
|
22
|
+
5. Warmup updates are reset before the measured phase so verification has a clean baseline.
|
|
23
|
+
6. With `--verify`, the final values are compared against the expected launch count.
|
|
24
|
+
|
|
25
|
+
## Command Examples
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pantheon --test atomic_virus --gpu 0 --duration 30 --mem 60
|
|
29
|
+
pantheon --test atomic_virus --gpu 0 --duration 30 --mem 60 --verify
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Runtime Parameters
|
|
33
|
+
|
|
34
|
+
| Parameter | Default | Effect |
|
|
35
|
+
| :--- | ---: | :--- |
|
|
36
|
+
| `block_size` | `256` | Threads per block. |
|
|
37
|
+
| `grid_size` | `0` | Number of blocks. `0` means auto-calculate from occupancy. |
|
|
38
|
+
| `kernel_loops` | `100` | Atomic adds per visited integer. |
|
|
39
|
+
| `warmup_iters` | `5` | Warmup launches before telemetry, followed by a memory reset. |
|
|
40
|
+
| `sync_mode` | `2` | `0=Spin`, `1=Yield`, `2=Block`. |
|
|
41
|
+
| `init_pattern` | `0` | `0=Zeroes`, `1=Ones`. |
|
|
42
|
+
|
|
43
|
+
## Output And Interpretation
|
|
44
|
+
|
|
45
|
+
`Throughput` is reported in `MAPS`: million atomic operations per second. Higher MAPS means more atomic traffic, but the most stressful configuration may be the one that produces higher power, temperature, or throttle signals rather than raw throughput.
|
|
46
|
+
|
|
47
|
+
## Failure Signals
|
|
48
|
+
|
|
49
|
+
| Symptom | Possible meaning |
|
|
50
|
+
| :--- | :--- |
|
|
51
|
+
| Verification mismatch | Atomic count mismatch, cache/L2 fault, or injected corruption. |
|
|
52
|
+
| Very low MAPS | Severe atomic serialization, throttling, or too much memory footprint. |
|
|
53
|
+
| High power with modest MAPS | Useful stress point: the fabric is busy even when progress is slow. |
|
|
54
|
+
|
|
55
|
+
## Source
|
|
56
|
+
|
|
57
|
+
The implementation lives in [`atomic_virus.cpp`](./atomic_virus.cpp).
|