vimarsh's picture
download
raw
16.4 kB
#!/usr/bin/env python3
"""Independent claim checks and scaled CUDA proxies for SlideSparse."""
from __future__ import annotations
import argparse
import csv
import itertools
import json
import math
import random
import re
import statistics
import time
from pathlib import Path
HERE = Path(__file__).resolve().parent
SOURCE_DATA = HERE / "source_data"
def emit(record: dict) -> None:
print("RESULT_JSON " + json.dumps(record, sort_keys=True), flush=True)
def windows(n: int) -> list[tuple[int, ...]]:
return [tuple(range(2 * j, 2 * j + 4)) for j in range(n - 1)]
def greedy_assign(mask: tuple[int, ...], n: int) -> list[list[int]]:
"""Mirror the released offline packer's left-to-right allocation."""
assigned: set[int] = set()
result: list[list[int]] = []
for window in windows(n):
chosen = [i for i in window if mask[i] and i not in assigned][:2]
result.append(chosen)
assigned.update(chosen)
return result
def exact_assignment_exists(mask: tuple[int, ...], n: int) -> bool:
nonzeros = [i for i, value in enumerate(mask) if value]
candidates = [[j for j, window in enumerate(windows(n)) if i in window] for i in nonzeros]
order = sorted(range(len(nonzeros)), key=lambda idx: len(candidates[idx]))
capacity = [2] * (n - 1)
def search(depth: int) -> bool:
if depth == len(order):
return True
idx = order[depth]
for window_id in candidates[idx]:
if capacity[window_id]:
capacity[window_id] -= 1
if search(depth + 1):
return True
capacity[window_id] += 1
return False
return search(0)
def coverage_audit(max_n: int, seed: int) -> dict:
rng = random.Random(seed)
rows = []
inner_product_trials = 0
max_error = 0.0
for n in range(2, max_n + 1):
length = 2 * n
nonzero_count = 2 * n - 2
patterns = 0
exact_failures = 0
greedy_failures = 0
for nz_positions in itertools.combinations(range(length), nonzero_count):
mask_list = [0] * length
for index in nz_positions:
mask_list[index] = 1
mask = tuple(mask_list)
patterns += 1
if not exact_assignment_exists(mask, n):
exact_failures += 1
allocation = greedy_assign(mask, n)
assigned = {index for group in allocation for index in group}
if len(assigned) != nonzero_count:
greedy_failures += 1
weights = [rng.uniform(-2, 2) if mask[i] else 0.0 for i in range(length)]
activations = [rng.uniform(-2, 2) for _ in range(length)]
direct = sum(w * x for w, x in zip(weights, activations))
lifted = sum(weights[i] * activations[i] for group in allocation for i in group)
max_error = max(max_error, abs(direct - lifted))
inner_product_trials += 1
rows.append(
{
"N": n,
"block": f"{2*n-2}:{2*n}",
"windows": n - 1,
"patterns": patterns,
"exact_failures": exact_failures,
"greedy_failures": greedy_failures,
"fewer_windows_capacity": 2 * max(0, n - 2),
"required_nonzeros": nonzero_count,
}
)
return {
"claim": 1,
"status": "verified" if all(r["exact_failures"] == r["greedy_failures"] == 0 for r in rows) else "failed",
"rows": rows,
"inner_product_trials": inner_product_trials,
"max_abs_inner_product_error": max_error,
"necessity_reason": "K=N-2 has capacity 2N-4 < 2N-2",
}
def theory_audit() -> dict:
rows = []
expected = {3: 1.5, 4: 4 / 3, 5: 1.25}
for n, target in expected.items():
gamma = 2 * (n - 1) / n
speedup = 2 / gamma
rows.append(
{
"N": n,
"pattern": f"{2*n-2}:{2*n}",
"expansion_gamma": gamma,
"derived_speedup": speedup,
"reported_speedup": target,
"abs_error": abs(speedup - target),
}
)
return {"claim": 2, "status": "verified", "rows": rows, "limit_as_N_to_infinity": 1.0}
def read_csv(name: str) -> list[dict[str, str]]:
with (SOURCE_DATA / name).open(newline="") as handle:
return list(csv.DictReader(handle))
def nonempty_float(value: str | None) -> float | None:
if value is None or value.strip() == "":
return None
return float(value)
def source_table_audit() -> list[dict]:
prefill_abs = read_csv("A100_prefill_absolute_Qwen2.5-7B-INT8.csv")
prefill_speed = read_csv("A100_prefill_speedup_Qwen2.5-7B-INT8.csv")
row_abs = next(row for row in prefill_abs if row["M"] == "8192")
row_speed = next(row for row in prefill_speed if row["M"] == "8192")
ratio = float(row_abs["cusparse_2_8"]) / float(row_abs["cuBLAS"])
claim3 = {
"claim": 3,
"status": "not_exactly_reproduced",
"M": 8192,
"dense_tokens_s": float(row_abs["cuBLAS"]),
"slidesparse_6_8_tokens_s": float(row_abs["cusparse_2_8"]),
"recomputed_speedup": ratio,
"released_rounded_table": float(row_speed["cusparse_2_8"]),
"paper_value": 1.33,
"note": "Released source data recomputes to 1.3186x (1.32x at two decimals), not exactly 1.33x.",
}
a100 = read_csv("A100_kernel_INT8_total_Qwen2.5-7B.csv")
rtx = read_csv("RTX4090_kernel_FP8_total_Qwen2.5-7B.csv")
h100 = read_csv("H100_kernel_FP8_total_Qwen2.5-7B.csv")
a100_large = [float(r["cuSPARSE_2_8"]) for r in a100 if int(r["M"]) in (8192, 16384)]
rtx_large = [float(r["cuSPARSE_2_8"]) for r in rtx if int(r["M"]) in (4096, 16384)]
h100_large = [float(r["cuSPARSE_2_8"]) for r in h100 if int(r["M"]) in (8192, 16384)]
claim4 = {
"claim": 4,
"status": "partly_verified_but_hardware_attribution_refuted",
"A100_INT8_6_8_M8192_16384": a100_large,
"RTX4090_FP8_6_8_M4096_16384": rtx_large,
"H100_FP8_6_8_M8192_16384": h100_large,
"paper_text": "RTX 4090 FP8 achieves 1.35-1.37x; it does not attribute that range to H100.",
}
accuracy_rows = read_csv("reported_Qwen3_reasoning_accuracy.csv")
recomputed = {}
for row in accuracy_rows:
values = [float(row[key]) for key in ("GSM8K", "MATH-500", "HumanEval")]
recomputed[row["regime"]] = {"mean": statistics.mean(values), "reported_average": float(row["Average"])}
claim5 = {
"claim": 5,
"status": "arithmetic_verified_experiment_not_reproducible_from_release",
"averages": recomputed,
"note": "The release lacks the Qwen3 fine-tuned checkpoints and exact training recipe; its accuracy_eval workflow targets different models/tasks.",
}
decode_records = []
for path in sorted(SOURCE_DATA.glob("*_decode_speedup_*.csv")):
gpu, _, _, model = path.stem.split("_", 3)
for row in read_csv(path.name):
value = nonempty_float(row.get("cusparse_2_8"))
if value is not None:
decode_records.append({"gpu": gpu, "model": model, "M": int(row["M"]), "speedup": value})
within_headline = [r for r in decode_records if 1.07 <= r["speedup"] <= 1.21]
claim6 = {
"claim": 6,
"status": "qualified",
"all_released_decode_6_8_range": [min(r["speedup"] for r in decode_records), max(r["speedup"] for r in decode_records)],
"records_in_paper_headline_range": len(within_headline),
"total_records": len(decode_records),
"note": "The 1.07-1.21x prose summarizes selected configurations; the full released tables include values outside it.",
}
return [claim3, claim4, claim5, claim6]
def run_baseline(config: dict) -> None:
emit({"event": "environment", "mode": "baseline", "python": __import__("sys").version})
emit(coverage_audit(int(config["coverage_max_n"]), int(config["seed"])))
emit(theory_audit())
for record in source_table_audit():
emit(record)
def cuda_time_ms(fn, warmup: int, repeats: int) -> tuple[float, float]:
import torch
for _ in range(warmup):
fn()
torch.cuda.synchronize()
samples = []
for _ in range(repeats):
start = torch.cuda.Event(enable_timing=True)
end = torch.cuda.Event(enable_timing=True)
start.record()
fn()
end.record()
end.synchronize()
samples.append(float(start.elapsed_time(end)))
return statistics.median(samples), statistics.mean(samples)
def run_hardware(config: dict) -> None:
import torch
from torch.sparse import to_sparse_semi_structured
if not torch.cuda.is_available():
raise RuntimeError("CUDA is required for the hardware benchmark")
h = config["hardware"]
torch.manual_seed(int(config["seed"]))
device = torch.device("cuda:0")
props = torch.cuda.get_device_properties(device)
requested_dtype = h["dtype"]
dtype = torch.int8 if requested_dtype == "int8" else torch.float16
n = int(h["n"])
k = int(h["k"])
k_slid = 3 * k // 2
warmup = int(h["warmup"])
repeats = int(h["repeats"])
dense_w = torch.randint(-8, 8, (n, k), device=device, dtype=torch.int8) if dtype == torch.int8 else torch.randn((n, k), device=device, dtype=dtype)
sparse_w_dense = torch.randint(-8, 8, (n, k_slid), device=device, dtype=torch.int8) if dtype == torch.int8 else torch.randn((n, k_slid), device=device, dtype=dtype)
sparse_w_dense[:, 2::4] = 0
sparse_w_dense[:, 3::4] = 0
sparse_w = to_sparse_semi_structured(sparse_w_dense)
emit(
{
"event": "environment",
"mode": "hardware",
"gpu": props.name,
"compute_capability": f"{props.major}.{props.minor}",
"memory_gib": props.total_memory / 2**30,
"torch": torch.__version__,
"cuda": torch.version.cuda,
"requested_dtype": requested_dtype,
}
)
for m in h["m_values"]:
m = int(m)
if dtype == torch.int8:
dense_x = torch.randint(-8, 8, (m, k), device=device, dtype=dtype)
sparse_x = torch.randint(-8, 8, (m, k_slid), device=device, dtype=dtype)
dense_fn = lambda: torch._int_mm(dense_x, dense_w.t())
else:
dense_x = torch.randn((m, k), device=device, dtype=dtype)
sparse_x = torch.randn((m, k_slid), device=device, dtype=dtype)
dense_fn = lambda: torch.mm(dense_x, dense_w.t())
sparse_fn = lambda: torch.mm(sparse_x, sparse_w.t())
dense_median, dense_mean = cuda_time_ms(dense_fn, warmup, repeats)
sparse_median, sparse_mean = cuda_time_ms(sparse_fn, warmup, repeats)
emit(
{
"claim": 4 if m >= 512 else 6,
"status": "scaled_hardware_proxy",
"M": m,
"N": n,
"K_dense": k,
"K_slid": k_slid,
"dtype": str(dtype).replace("torch.", ""),
"dense_median_ms": dense_median,
"dense_mean_ms": dense_mean,
"slidesparse_sparse_median_ms": sparse_median,
"slidesparse_sparse_mean_ms": sparse_mean,
"speedup": dense_median / sparse_median,
"theory_6_8": 4 / 3,
"scope": "PyTorch semi-structured sparse GEMM proxy; excludes fused activation lifting and vLLM.",
}
)
def magnitude_prune_model(model, pattern: str) -> dict:
import torch
keep, total = map(int, pattern.split(":"))
prune = total - keep
zeroed = 0
elements = 0
with torch.no_grad():
for module in model.modules():
if not isinstance(module, torch.nn.Linear):
continue
weight = module.weight.data
usable = weight.shape[1] - weight.shape[1] % total
if usable == 0:
continue
view = weight[:, :usable].reshape(weight.shape[0], -1, total)
indices = view.abs().topk(prune, dim=-1, largest=False).indices
view.scatter_(-1, indices, 0)
zeroed += indices.numel()
elements += view.numel()
return {"pattern": pattern, "zeroed": zeroed, "eligible_elements": elements, "sparsity": zeroed / elements}
def extract_answer(text: str) -> str | None:
matches = re.findall(r"-?\d+(?:\.\d+)?", text.replace(",", ""))
return matches[-1] if matches else None
def run_accuracy_proxy(config: dict) -> None:
import torch
from datasets import load_dataset
from transformers import AutoModelForCausalLM, AutoTokenizer
if not torch.cuda.is_available():
raise RuntimeError("CUDA is required for the accuracy proxy")
a = config["accuracy"]
model_id = a["model"]
samples = list(load_dataset("openai/gsm8k", "main", split=f"test[:{int(a['num_samples'])}]"))
tokenizer = AutoTokenizer.from_pretrained(model_id)
emit({"event": "environment", "mode": "accuracy_proxy", "gpu": torch.cuda.get_device_name(0), "model": model_id, "samples": len(samples)})
for pattern in a["patterns"]:
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.bfloat16, device_map="cuda").eval()
pruning = None if pattern == "dense" else magnitude_prune_model(model, pattern)
correct = 0
predictions = []
answer_nlls = []
started = time.time()
for item in samples:
prompt = "Solve the following problem. Give the final numeric answer clearly.\n\n" + item["question"]
messages = [{"role": "user", "content": prompt}]
rendered = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True, enable_thinking=False)
inputs = tokenizer(rendered, return_tensors="pt").to("cuda")
with torch.inference_mode():
teacher = tokenizer(rendered + "\n" + item["answer"], return_tensors="pt").to("cuda")
labels = teacher.input_ids.clone()
labels[:, : inputs.input_ids.shape[1]] = -100
answer_nll = float(model(**teacher, labels=labels).loss)
output = model.generate(**inputs, max_new_tokens=int(a["max_new_tokens"]), do_sample=False, pad_token_id=tokenizer.eos_token_id)
completion = tokenizer.decode(output[0, inputs.input_ids.shape[1] :], skip_special_tokens=True)
predicted = extract_answer(completion)
gold = extract_answer(item["answer"].split("####")[-1])
is_correct = predicted == gold
correct += int(is_correct)
answer_nlls.append(answer_nll)
predictions.append({"predicted": predicted, "gold": gold, "correct": is_correct, "answer_nll": answer_nll})
mean_answer_nll = statistics.mean(answer_nlls)
emit(
{
"claim": 5,
"status": "scaled_proxy",
"model": model_id,
"pattern": pattern,
"benchmark": "GSM8K first contiguous test examples, zero-shot",
"samples": len(samples),
"correct": correct,
"accuracy_percent": 100 * correct / len(samples),
"mean_teacher_forced_answer_nll": mean_answer_nll,
"answer_perplexity": math.exp(min(mean_answer_nll, 20.0)),
"pruning": pruning,
"elapsed_s": time.time() - started,
"predictions": predictions,
"scope": "0.6B magnitude-pruned proxy, not the paper's fine-tuned Qwen3 setup or three-task average.",
}
)
del model
torch.cuda.empty_cache()
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--config", type=Path, required=True)
args = parser.parse_args()
config = json.loads(args.config.read_text())
mode = config["mode"]
if mode == "baseline":
run_baseline(config)
elif mode == "hardware":
run_hardware(config)
elif mode == "accuracy_proxy":
run_accuracy_proxy(config)
else:
raise ValueError(f"Unknown mode: {mode}")
if __name__ == "__main__":
main()

Xet Storage Details

Size:
16.4 kB
·
Xet hash:
430938d38701cf24372ca958d061289344daffefb66444272d40b40b8b6e1f79

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.