gem/scripts/analyze-bq8-bk128-gate.sh
2026-08-30 18:24:55 -06:00

218 lines
10 KiB
Bash
Executable file

#!/bin/sh
set -eu
[ "$#" -eq 1 ] || [ "$#" -eq 2 ] || {
echo "usage: $0 GATE_DIRECTORY [OUTPUT_JSON]" >&2
exit 2
}
directory=$1
output=${2:-}
[ -s "$directory/gate-config.json" ] || {
echo "gem: missing BK128 gate input: $directory/gate-config.json" >&2
exit 2
}
[ -s "$directory/metal-device.json" ] || {
echo "gem: missing BK128 gate input: $directory/metal-device.json" >&2
exit 2
}
for path in "$directory/sha256.txt" "$directory/native-linkage.txt" \
"$directory/macos-version.txt" "$directory/run-start-utc.txt"; do
[ -s "$path" ] || { echo "gem: missing BK128 provenance input: $path" >&2; exit 2; }
done
for tokens in 4096 65536 131072; do
for path in "$directory/seeded-$tokens.json" \
"$directory/host-preflight-$tokens.json" \
"$directory/host-postflight-$tokens.json"; do
[ -s "$path" ] || { echo "gem: missing BK128 gate input: $path" >&2; exit 2; }
done
done
analyze()
{
jq -n \
--slurpfile cfg "$directory/gate-config.json" \
--slurpfile device "$directory/metal-device.json" \
--slurpfile r4 "$directory/seeded-4096.json" \
--slurpfile r64 "$directory/seeded-65536.json" \
--slurpfile r128 "$directory/seeded-131072.json" \
--slurpfile h4 "$directory/host-preflight-4096.json" \
--slurpfile h64 "$directory/host-preflight-65536.json" \
--slurpfile h128 "$directory/host-preflight-131072.json" \
--slurpfile p4 "$directory/host-postflight-4096.json" \
--slurpfile p64 "$directory/host-postflight-65536.json" \
--slurpfile p128 "$directory/host-postflight-131072.json" \
--rawfile sha "$directory/sha256.txt" \
--rawfile linkage "$directory/native-linkage.txt" \
--rawfile macos "$directory/macos-version.txt" \
--rawfile started "$directory/run-start-utc.txt" '
def mean($xs): $xs | add / length;
def sample_stddev($xs):
mean($xs) as $m |
(($xs | map((. - $m) * (. - $m)) | add) / (($xs | length) - 1)) | sqrt;
def cv_pct($xs): 100 * sample_stddev($xs) / mean($xs);
def abs: if . < 0 then -. else . end;
def host_ok($h):
$h.schema_version == 2 and $h.status == "pass" and
$h.logical_cpus > 0 and $h.load_per_logical_cpu >= 0 and
$h.load_per_logical_cpu <= $h.max_load_per_logical_cpu and
$h.recent_aggregate_process_cpu_percent >= 0 and
$h.short_window_busy_cpu_percent >= 0 and
$h.short_window_busy_cpu_percent <= $h.max_short_window_busy_cpu_percent and
$h.ac_power == true and $h.no_thermal_warning == true and
($h.power_state | type) == "string" and
($h.power_state | contains("AC Power")) and
($h.thermal_state | type) == "string";
def valid_run($r; $position; $capacity):
$r.schema_version == 1 and $r.engine == "gem-native-c-metal" and
$r.shape == "31b-full" and $r.kv_type == "f32" and
$r.position == $position and $r.rows == 32 and
$r.cache_capacity == $capacity and $r.seed_prefix == true and
$r.repetitions == 10 and $r.warmup == 10 and
$r.bq8_pipeline_resources == {
thread_execution_width: 32,
max_total_threads_per_threadgroup: 1024,
static_threadgroup_memory_bytes: 0,
dynamic_threadgroup_memory_bytes: 18496,
threads_per_threadgroup: 256
} and
$r.bq8_bk128_pipeline_resources == {
thread_execution_width: 32,
max_total_threads_per_threadgroup: 1024,
static_threadgroup_memory_bytes: 0,
dynamic_threadgroup_memory_bytes: 20544,
threads_per_threadgroup: 256
} and
($r.variants["online-bq8"].samples | length) == $r.repetitions and
($r.variants["online-bq8-bk128"].samples | length) == $r.repetitions and
($r.variants["online-bq8"].samples | all(. > 0)) and
($r.variants["online-bq8-bk128"].samples | all(. > 0));
def metrics($r):
$r.variants["online-bq8"].samples as $b |
$r.variants["online-bq8-bk128"].samples as $c |
[range(0; $b | length) as $i | 100 * (1 - $c[$i] / $b[$i])] as $paired |
($b | length / 2) as $half |
mean($b) as $bm | mean($c) as $cm |
cv_pct($b) as $bcv | cv_pct($c) as $ccv |
mean($paired) as $paired_mean |
sample_stddev($paired) as $paired_stddev |
(100 * (1 - $cm / $bm)) as $gain |
(100 * (mean($b[$half:]) / mean($b[0:$half]) - 1)) as $bdrift |
(100 * (mean($c[$half:]) / mean($c[0:$half]) - 1)) as $cdrift |
{
baseline_mean_seconds: $bm,
candidate_mean_seconds: $cm,
improvement_pct: $gain,
baseline_cv_pct: $bcv,
candidate_cv_pct: $ccv,
observed_noise_floor_pct: ([$bcv, $ccv] | max),
paired_improvement_mean_pct: $paired_mean,
paired_improvement_stddev_pct: $paired_stddev,
paired_improvement_95ci_lower_pct:
($paired_mean - 2.262 * $paired_stddev / (($paired | length) | sqrt)),
baseline_half_drift_pct: $bdrift,
candidate_half_drift_pct: $cdrift,
stable: ($bcv <= 3 and $ccv <= 3 and
($bdrift | abs) <= 3 and ($cdrift | abs) <= 3),
every_candidate_sample_faster: (($c | max) < ($b | min))
};
$r4[0] as $a | $r64[0] as $b | $r128[0] as $c |
if (($sha | contains("gem-long-attention-profile")) and
($sha | contains("gem-metal-counter-info")) and
($sha | contains("src/metal.m")) and
($sha | contains("src/long_attention_profile.c")) and
($sha | contains("scripts/run-bq8-bk128-gate.sh")) and
($sha | contains("scripts/analyze-bq8-bk128-gate.sh")) and
($linkage | contains("gem-long-attention-profile")) and
($linkage | contains("gem-metal-counter-info")) and
(($linkage | test("lib(llama|ggml)"; "i")) | not) and
($macos | length) > 1 and
($started | test("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z")) and
$device[0].schema_version == 1 and
$device[0].workload == "metal-counter-info" and
$device[0].device.name == "Apple M4 Max" and
$device[0].device.has_unified_memory == true and
$device[0].device.gpu_families.apple9 == true and
$device[0].device.max_threadgroup_memory_bytes >= 20544 and
$cfg[0].schema_version == 2 and $cfg[0].engine == "gem-native-c-metal" and
$cfg[0].axis == {name: "cache_tile_width", baseline: 64, candidate: 128} and
$cfg[0].positions == [4064, 65504, 131040] and $cfg[0].rows == 32 and
$cfg[0].kv_type == "f32" and $cfg[0].repetitions == 10 and
$cfg[0].warmup == 10 and $cfg[0].seed_prefix == true and
$cfg[0].max_load_per_logical_cpu > 0 and
$cfg[0].max_short_window_busy_cpu_percent > 0 and
$cfg[0].requires_ac_power == true and
$cfg[0].requires_no_thermal_warning == true and
valid_run($a; 4064; 4096) and valid_run($b; 65504; 65536) and
valid_run($c; 131040; 131072) and host_ok($h4[0]) and
host_ok($h64[0]) and host_ok($h128[0]) and host_ok($p4[0]) and
host_ok($p64[0]) and host_ok($p128[0])) | not
then error("BK128 inputs violate the frozen one-axis gate controls")
else
metrics($a) as $m4 | metrics($b) as $m64 | metrics($c) as $m128 |
($m4.stable and $m64.stable and $m128.stable) as $stable |
(($m4.improvement_pct >= -2 or
(-$m4.improvement_pct) <= $m4.observed_noise_floor_pct)) as $short_guard |
(($m64.improvement_pct >= 3 and
$m64.improvement_pct > $m64.observed_noise_floor_pct and
$m64.paired_improvement_95ci_lower_pct > 0) and
($m128.improvement_pct >= 3 and
$m128.improvement_pct > $m128.observed_noise_floor_pct and
$m128.paired_improvement_95ci_lower_pct > 0)) as $long_gain |
($stable and $short_guard and $long_gain) as $advance |
{
schema_version: 2,
scope: "seeded exact-shape 31B/F32 global-prefill attention operator screen",
axis: {name: "cache_tile_width", baseline: 64, candidate: 128},
provenance: {
run_started_utc: ($started | rtrimstr("\n")),
macos_version: ($macos | rtrimstr("\n")),
sha256_receipt: ($sha | rtrimstr("\n")),
native_linkage_receipt: ($linkage | rtrimstr("\n"))
},
frozen_controls: {
query_rows: 8, measured_rows: 32, kv_type: "f32",
repetitions: 10, warmup: 10, seeded_prefix: true,
maximum_cv_pct: 3, maximum_half_drift_pct: 3,
minimum_long_context_improvement_pct: 3,
paired_confidence_level_pct: 95,
paired_t_critical_df9: 2.262,
maximum_4k_regression_pct: 2
},
threadgroup_memory: {
device_limit_bytes: $device[0].device.max_threadgroup_memory_bytes,
baseline_bytes: 18496,
candidate_bytes: 20544,
baseline_limit_utilization_pct:
(100 * 18496 / $device[0].device.max_threadgroup_memory_bytes),
candidate_limit_utilization_pct:
(100 * 20544 / $device[0].device.max_threadgroup_memory_bytes),
candidate_additional_bytes: 2048,
occupancy_inference: "not available from the public per-threadgroup limit"
},
host_preflight: {
at_4096: $h4[0], at_65536: $h64[0], at_131072: $h128[0]
},
host_postflight: {
at_4096: $p4[0], at_65536: $p64[0], at_131072: $p128[0]
},
contexts: {at_4096: $m4, at_65536: $m64, at_131072: $m128},
screening: {
stable: $stable,
short_context_guard_passed: $short_guard,
long_context_gain_passed: $long_gain,
advance_to_evaluated_graph_gate: $advance,
promotion_eligible: false,
decision: (if $advance then "advance_to_evaluated_graph_gate"
else "keep_bk64_and_remove_bk128" end),
reason: (if $advance
then "operator screen passed; evaluated-graph/logit and correctness gates remain required"
else "operator screen failed stability, 4K regression, or both 64K and 128K gain requirements"
end)
}
}
end'
}
if [ -n "$output" ]; then analyze > "$output"; else analyze; fi