gem/scripts/run-prefill-stage-attribution.sh
2026-08-30 18:24:55 -06:00

90 lines
3.9 KiB
Bash
Executable file

#!/bin/sh
# Capture a production-route long-prefill stage profile before selecting a kernel axis.
set -eu
if [ "$#" -ne 2 ]; then
echo "usage: $0 MODEL.gguf OUTPUT_DIR" >&2
exit 2
fi
model=$1
output=$2
build_dir=${GEM_PREFILL_STAGE_BUILD:-build}
[ -r "$model" ] || { echo "gem: model is not readable: $model" >&2; exit 2; }
[ ! -e "$output" ] || { echo "gem: output already exists: $output" >&2; exit 2; }
if command -v pgrep >/dev/null 2>&1 && pgrep -f '[/]gem serve' >/dev/null 2>&1; then
echo "gem: refusing to overlap a running native server Metal workload" >&2
exit 1
fi
for binary in "$build_dir/gem" "$build_dir/gem-long-prefill-stage-profile" \
"$build_dir/gem-metal-counter-info"; do
[ -x "$binary" ] || { echo "gem: required binary is missing: $binary" >&2; exit 2; }
done
model_sha256=${GEM_MODEL_SHA256:-}
if [ -n "$model_sha256" ]; then
case $model_sha256 in *[!0-9a-fA-F]*) model_sha256=invalid ;; esac
[ "${#model_sha256}" -eq 64 ] || {
echo "gem: GEM_MODEL_SHA256 must contain exactly 64 hexadecimal digits" >&2
exit 2
}
fi
actual_model_sha256=$(shasum -a 256 "$model" | awk '{print $1}')
if [ -n "$model_sha256" ] && [ "$actual_model_sha256" != "$model_sha256" ]; then
echo "gem: model digest does not match GEM_MODEL_SHA256" >&2
exit 1
fi
model_sha256=$actual_model_sha256
mkdir -p "$output"
date -u '+%Y-%m-%dT%H:%M:%SZ' > "$output/run-start-utc.txt"
printf '%s %s\n' "$model_sha256" "$model" > "$output/model-sha256.txt"
sw_vers > "$output/macos-version.txt"
shasum -a 256 "$build_dir/gem" "$build_dir/gem-long-prefill-stage-profile" \
"$build_dir/gem-metal-counter-info" > "$output/binary-sha256.txt"
otool -L "$build_dir/gem" "$build_dir/gem-long-prefill-stage-profile" \
"$build_dir/gem-metal-counter-info" > "$output/native-linkage.txt"
if grep -Eiq 'lib(llama|ggml)' "$output/native-linkage.txt"; then
echo "gem: production profiler unexpectedly links llama/ggml" >&2
exit 1
fi
"$build_dir/gem" print-config --model "$model" --context 131328 --kv-type f32 \
> "$output/print-config.json"
jq -e '
.engine == "gem-native-c-metal" and .model_size == 31 and .layers == 60 and
.context == 131328 and .kv_type == "f32" and .kv_bytes == 23194501120 and
(.attention_kernel | contains("31b-full-online-row2")) and
(.attention_kernel | contains("full-decode-value-dim32-split16@8192"))
' "$output/print-config.json" >/dev/null
"$build_dir/gem-metal-counter-info" > "$output/metal-counter-info.json"
jq -e '
.schema_version == 1 and .workload == "metal-counter-info" and
.device.has_unified_memory == true and .device.gpu_families.apple9 == true and
.device.max_threadgroup_memory_bytes >= 32768 and
.sampling_points.stage_boundary == true and
(.counter_sets | type == "array" and length > 0)
' "$output/metal-counter-info.json" >/dev/null
scripts/analyze-metal-counter-info.sh "$output/metal-counter-info.json" \
"$output/metal-counter-analysis.json"
jq -e '
.instrumentation.stage_timestamps_available == true and
(.instrumentation.selected == "public-dispatch-counter-sampling" or
.instrumentation.selected == "stage-timestamps-plus-xcode-gpu-trace")
' "$output/metal-counter-analysis.json" >/dev/null
"$build_dir/gem-long-prefill-stage-profile" "$model" 131040 3 --seed-prefix \
> "$output/prefill-stage-seeded-131040.json"
scripts/analyze-prefill-stages.sh "$output/prefill-stage-seeded-131040.json" \
"$output/prefill-stage-analysis-131040.json"
jq -e '
.screening.promotion_eligible == false and
.source.layer_count == 60 and .source.sliding_layer_count == 50 and
.source.full_layer_count == 10 and .source.repetitions == 3 and
.source.prefix_mode == "seeded-kv" and .source.prefix_tokens == 131040 and
.source.tile_tokens == 32
' "$output/prefill-stage-analysis-131040.json" >/dev/null
date -u '+%Y-%m-%dT%H:%M:%SZ' > "$output/run-end-utc.txt"
jq . "$output/prefill-stage-analysis-131040.json"