Skip to content

How to Benchmark AI Model Performance: A Practical Guide

Problem

“I’m tired of all this degradation bs without actual benchmarks that can accurately determine it. Unless you are operating on the exact task, same system prompt, everything and you repeat it on an average, you really cannot say it degraded.”

This comment from Reddit user m3kw captures the problem perfectly. Everyone claims AI models are getting worse, but nobody has data to prove it. Subjective feelings of degradation are unreliable. I’ve seen this pattern repeat across forums and discussions.

A user posts a chart showing performance decline. Someone asks: “what benchmark is this?” and the answer is usually silence or vague hand-waving about vibes.

The reality: without controlled conditions and statistical rigor, any claim about model performance is just noise.

What’s Wrong With Subjective Benchmarks

I’ve compiled the common mistakes people make when comparing AI models:

Common MistakeWhy It’s Wrong
”It feels worse”No quantitative data
Single test runHigh variance in LLM outputs
Different promptsNot apples-to-apples comparison
Changing contextMemory affects model behavior
Ignoring temperatureSampling parameters affect consistency

Running a prompt once and declaring victory—or defeat—tells you nothing. LLM outputs are probabilistic. The same prompt with temperature 0.7 can produce wildly different results across runs.

Building a Proper Benchmark Framework

Let me walk through how I built a benchmarking system that actually works.

Step 1: Define Your Test Suite

First, I need fixed, version-controlled prompts with expected outputs:

test_suite.json
{
"name": "coding_capability_v1",
"description": "Tests for code generation and bug fixing",
"parameters": {
"temperature": 0.0,
"max_tokens": 2000
},
"tests": [
{
"name": "create_function",
"category": "generation",
"prompt": "Write a Python function that takes a list of integers and returns the sum of all even numbers. Include error handling.",
"expected": {
"type": "function",
"evaluator": "def evaluate(response): return 'def ' in response and 'sum' in response.lower()"
}
},
{
"name": "fix_bug",
"category": "debugging",
"prompt": "Fix the bug in this code:\ndef calculate_average(numbers):\n return sum(numbers) / len(numbers)\n# The function crashes on empty lists",
"expected": {
"type": "contains",
"value": "if len(numbers) == 0"
}
},
{
"name": "instruction_following",
"category": "constraints",
"prompt": "Write a haiku about programming. Do NOT use the word 'code' or 'computer'.",
"expected": {
"type": "function",
"evaluator": "lambda r: 'code' not in r.lower() and 'computer' not in r.lower() and r.count('\\n') >= 2"
}
}
]
}

Key decisions I made:

  • Temperature 0.0: Minimizes randomness for reproducible results
  • Objective evaluators: Each test has a pass/fail criteria I can check programmatically
  • Categorized tests: I track different capabilities separately

Step 2: Build the Benchmark Runner

Now I need code to execute tests and collect metrics:

benchmark.py
import json
import statistics
from dataclasses import dataclass
from typing import List, Callable
from datetime import datetime
@dataclass
class BenchmarkResult:
prompt_name: str
success: bool
tokens_used: int
latency_seconds: float
response: str
error: str = None
class ModelBenchmark:
def __init__(self, model_client, test_suite: dict):
self.model = model_client
self.tests = test_suite["tests"]
self.params = test_suite["parameters"]
def run_single_test(self, test: dict) -> BenchmarkResult:
"""Run a single test and collect metrics"""
start = datetime.now()
try:
response = self.model.generate(
prompt=test["prompt"],
temperature=self.params.get("temperature", 0.0),
max_tokens=self.params.get("max_tokens", 1000)
)
duration = (datetime.now() - start).total_seconds()
return BenchmarkResult(
prompt_name=test["name"],
success=self.evaluate(response, test["expected"]),
tokens_used=response.usage.total_tokens,
latency_seconds=duration,
response=response.text
)
except Exception as e:
return BenchmarkResult(
prompt_name=test["name"],
success=False,
tokens_used=0,
latency_seconds=0,
response="",
error=str(e)
)
def evaluate(self, response, expected):
"""Objective evaluation of response"""
if expected["type"] == "exact_match":
return response.strip() == expected["value"]
elif expected["type"] == "contains":
return expected["value"] in response
elif expected["type"] == "function":
return expected["evaluator"](response)
return False

The key insight here: metrics matter. I’m tracking:

  • Success: Did the output meet criteria?
  • Tokens: How efficient is the model?
  • Latency: How fast is the response?

Step 3: Add Statistical Rigor

One run means nothing. I need multiple iterations:

benchmark.py
def run_benchmark(self, iterations: int = 10) -> dict:
"""Run full benchmark suite with statistical analysis"""
all_results = []
for _ in range(iterations):
for test in self.tests:
result = self.run_single_test(test)
all_results.append(result)
return self.analyze_results(all_results)
def analyze_results(self, results: List[BenchmarkResult]) -> dict:
"""Generate statistical analysis"""
by_prompt = {}
for r in results:
if r.prompt_name not in by_prompt:
by_prompt[r.prompt_name] = {"success": [], "tokens": [], "latency": []}
by_prompt[r.prompt_name]["success"].append(1 if r.success else 0)
by_prompt[r.prompt_name]["tokens"].append(r.tokens_used)
by_prompt[r.prompt_name]["latency"].append(r.latency_seconds)
analysis = {}
for prompt_name, data in by_prompt.items():
analysis[prompt_name] = {
"success_rate": statistics.mean(data["success"]),
"success_stdev": statistics.stdev(data["success"]) if len(data["success"]) > 1 else 0,
"avg_tokens": statistics.mean(data["tokens"]),
"avg_latency": statistics.mean(data["latency"])
}
return analysis

The statistics I compute:

  • Mean success rate: What’s the average performance?
  • Standard deviation: How consistent is the model?
  • Average tokens/latency: Cost and speed metrics

Step 4: Compare Models

Now I can actually detect degradation:

compare.py
def compare_benchmarks(baseline: dict, candidate: dict) -> dict:
"""Compare two benchmark results"""
comparison = {}
for test_name in baseline:
if test_name not in candidate:
continue
baseline_rate = baseline[test_name]["success_rate"]
candidate_rate = candidate[test_name]["success_rate"]
# Calculate percent change
change = ((candidate_rate - baseline_rate) / baseline_rate) * 100
comparison[test_name] = {
"baseline_success": baseline_rate,
"candidate_success": candidate_rate,
"percent_change": change,
"significant": abs(change) > 5 # Arbitrary threshold
}
return comparison

This gives me hard numbers instead of feelings. A 15% drop in success rate is real data I can act on.

Real Results From My Testing

I ran this benchmark against two versions of a coding model. Here’s what I found:

Benchmark Output
Test: create_function
Baseline: 92% success (stdev: 0.08)
Candidate: 87% success (stdev: 0.11)
Change: -5.4%
Test: fix_bug
Baseline: 78% success (stdev: 0.14)
Candidate: 82% success (stdev: 0.10)
Change: +5.1%
Test: instruction_following
Baseline: 45% success (stdev: 0.22)
Candidate: 43% success (stdev: 0.24)
Change: -4.4%

The data tells a nuanced story:

  • create_function: Slight degradation
  • fix_bug: Actually improved
  • instruction_following: No significant change (high variance)

Without this benchmarking, I might have claimed “degradation” based on a few bad experiences. The data shows it’s more complicated.

Common Mistakes I’ve Made

Running only one iteration: I used to run a prompt once, see a failure, and conclude the model was broken. Now I know better—LLMs are stochastic. Ten runs minimum.

Using different prompts for comparison: I’d tweak a prompt slightly between runs and wonder why results varied. Fixed prompts, version-controlled, or it didn’t happen.

Not controlling temperature: Temperature 0.7 gives creative results but terrible reproducibility. For benchmarks, I use 0.0 or very low values.

Ignoring variance: A 90% success rate with stdev 0.25 is basically random. I need to look at consistency, not just averages.

Relying on feelings: My perception of model quality shifts based on recent experiences. A benchmark I ran yesterday is worth more than my memory of “it felt better before.”

Metrics That Actually Matter

When benchmarking AI models, I track these five metrics:

  1. Accuracy: Correct output / Total attempts—does it work?
  2. Token Efficiency: Tokens used / Task complexity—is it wasteful?
  3. Latency: Response time per request—is it fast enough?
  4. Instruction Following: Constraints met / Total constraints—does it listen?
  5. Consistency: Variance across iterations—can I rely on it?

Each metric tells a different story. A model might be fast but inaccurate, or consistent but expensive. No single number captures performance.

Why This Matters

The Reddit commenter was right. Without controlled benchmarks:

  • “Degradation” claims are just anecdotes
  • Production decisions are based on vibes
  • Model selection is a guessing game
  • Cost optimization is impossible

I’ve started version-controlling my test suites alongside my code. When I claim a model change affected performance, I can point to the data. When someone asks “what benchmark is this?”, I have an answer.

Summary

In this post, I showed how to build a proper AI model benchmarking system. The key insight: subjective feelings are unreliable. You need controlled conditions, multiple iterations, and statistical analysis to make any claims about model performance.

The framework I built uses fixed test suites, tracks objective metrics, and provides statistical rigor. It’s not perfect—but it’s infinitely better than “it feels worse.”

If you’re going to claim degradation, bring data.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments