Benchmark Saturation › Code Implementation
Language AI HandbookAll chapters Benchmark Saturation › Code ImplementationOne section · Sections in this chapter8 / 13 ↓
Reading tools Definitions & inline links
Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.
Article links
Make inline references clickable
We'll analyze synthetic benchmark data representing model scores over time to demonstrate saturation detection techniques. The synthetic data follows logistic growth curves, which are characteristic of technology adoption and capability improvement: starting with slow progress, accelerating through a period of rapid improvement, and finally plateauing as capabilities approach physical or theoretical limits. This S-shaped trajectory describes the observed behavior of real benchmarks well, and will allow us to illustrate the statistical detection methods with controlled, reproducible data.
First, let's set up our analysis environment and generate representative data.
import numpy as np
import pandas as pd
# Set random seed for reproducibility
np.random.seed(42)
# Generate synthetic benchmark saturation data
# Simulating GLUE-like trajectory from 2018-2024
years = np.arange(2018, 2025.25, 0.25)
n_timepoints = len(years)
# Logistic growth curve for accuracy: starts low, accelerates, plateaus
def logistic_growth(t, L, k, t0):
"""Logistic growth: L is max, k is growth rate, t0 is midpoint"""
return L / (1 + np.exp(-k * (t - t0)))
# Parameters for different benchmarks
benchmarks = {
"GLUE (Easy)": {"L": 0.95, "k": 2.5, "t0": 2019.0, "noise": 0.01},
"SuperGLUE (Medium)": {"L": 0.92, "k": 2.0, "t0": 2020.5, "noise": 0.015},
"MMLU (Hard)": {"L": 0.94, "k": 1.8, "t0": 2022.0, "noise": 0.02},
"HumanEval (Very Hard)": {"L": 0.95, "k": 1.2, "t0": 2023.5, "noise": 0.03},
}
# Generate scores
data = {}
for name, params in benchmarks.items():
base_score = logistic_growth(years, params["L"], params["k"], params["t0"])
noise = np.random.normal(0, params["noise"], len(years))
data[name] = np.clip(base_score + noise, 0, 1)
df = pd.DataFrame(data, index=years)
df.index.name = "Year"The four benchmarks represent a spectrum of difficulty levels, each with different logistic growth parameters. GLUE has the steepest growth rate () and an early midpoint (2019), leading to rapid early saturation. HumanEval has the gentlest growth rate () and a late midpoint (2023.5), maintaining discriminative power longer. The added noise mimics the natural stochasticity in benchmark scores due to evaluation randomness and differences between model training runs.
(0.4, 1.0)

The visualization reveals distinct saturation phases. GLUE approaches its ceiling by 2020, while HumanEval continues to show growth through 2024. This demonstrates how benchmark difficulty extends useful lifespan. A benchmark's maximum score ceiling matters less than the rate at which it is approached; a benchmark with a 95% ceiling that takes a decade to reach is far more useful than one with a 90% ceiling that is reached in two years.
Next, let's calculate statistical indicators of saturation, specifically tracking the coefficient of variation over time to measure how much models are converging.
# Calculate rolling coefficient of variation to detect convergence
def rolling_cv(series, window=4):
"""Calculate coefficient of variation over rolling window"""
rolling_std = series.rolling(window=window).std()
rolling_mean = series.rolling(window=window).mean()
return rolling_std / rolling_mean
# Calculate CV for each benchmark
cv_data = {}
for col in df.columns:
cv_data[col] = rolling_cv(df[col])
cv_df = pd.DataFrame(cv_data, index=df.index)
The coefficient of variation plot shows GLUE's CV dropping to near zero by 2021, indicating that all top models perform identically on this benchmark. In contrast, HumanEval maintains higher variance, suggesting it still discriminates between model capabilities. The difference in CV trajectories directly reflects the difference in benchmark difficulty and how long each benchmark retains diagnostic value.
Now let's implement a function to detect when a benchmark has statistically saturated based on slope analysis and variance thresholds.
def detect_saturation(
years, scores, saturation_threshold=0.90, slope_threshold=0.02, window=4
):
"""
Detect saturation point based on:
1. Exceeding accuracy threshold
2. Slope falling below threshold (improvement slows)
3. Low variance in recent window
"""
# Calculate rolling slope (rate of improvement)
slopes = np.gradient(scores, years)
rolling_slope = pd.Series(slopes).rolling(window=window).mean().values
# Calculate rolling CV
rolling_std = pd.Series(scores).rolling(window=window).std().values
rolling_mean = pd.Series(scores).rolling(window=window).mean().values
rolling_cv = rolling_std / rolling_mean
# Find saturation point: where accuracy > threshold and slope < threshold
saturated_indices = []
for i in range(window, len(scores)):
if (
scores[i] > saturation_threshold
and abs(rolling_slope[i]) < slope_threshold
and rolling_cv[i] < 0.05
): # Less than 5% variation
saturated_indices.append(i)
if saturated_indices:
first_saturation = saturated_indices[0]
return {
"saturated": True,
"year": years[first_saturation],
"score": scores[first_saturation],
"indices": saturated_indices,
}
return {"saturated": False, "year": None, "score": None}
# Detect saturation for each benchmark
saturation_results = {}
for col in df.columns:
result = detect_saturation(df.index.values, df[col].values)
saturation_results[col] = resultThe detect_saturation function applies three simultaneous criteria. First, the accuracy must exceed the saturation threshold, so we only flag benchmarks where performance is high. Second, the rolling slope must fall below the slope threshold, capturing the slowdown in improvement rate that characterizes a plateau. Third, the rolling CV must be small, confirming that scores are converging rather than fluctuating. Requiring all three conditions together prevents false positives from temporary slowdowns or noise spikes.
Saturation Detection Results ================================================== GLUE (Easy): SATURATED at 2021.25 (score: 0.927) SuperGLUE (Medium): SATURATED at 2023.25 (score: 0.921) MMLU (Hard): SATURATED at 2024.75 (score: 0.923) HumanEval (Very Hard): NOT YET SATURATED
The detection algorithm confirms that GLUE saturates earliest (around 2020.25), while HumanEval remains unsaturated through the observation period. This matches the empirical observation that code generation benchmarks maintain discriminative power longer than natural language understanding tasks, because code execution provides an unambiguous correctness signal that is harder to game and requires new problem-solving ability for each distinct challenge.
Finally, let's simulate the effect of adversarial filtering (as used in HellaSwag) to show how dynamic difficulty adjustment extends benchmark lifespan.
# Simulate adversarial filtering extending benchmark lifespan
def simulate_adversarial_lifecycle(
years, initial_difficulty=0.5, adaptation_rate=0.3
):
"""
Simulate a benchmark that increases difficulty as models improve
"""
scores = []
difficulty = initial_difficulty
for year in years:
# Base capability improves over time (logistic)
base_capability = logistic_growth(year, 0.95, 1.5, 2021)
# Effective score is capability relative to current difficulty
# If capability > difficulty, model solves it, triggering difficulty increase
effective_score = min(base_capability / difficulty, 0.95) # Cap at 0.95
scores.append(effective_score)
# Adversarial adaptation: if score > 0.85, increase difficulty
if effective_score > 0.85:
difficulty = min(difficulty * (1 + adaptation_rate), 0.95)
return np.array(scores)
# Generate adversarial benchmark trajectory
adv_scores = simulate_adversarial_lifecycle(years)
# Compare to static benchmark
static_scores = logistic_growth(years, 0.95, 1.5, 2021) + np.random.normal(
0, 0.01, len(years)
)
static_scores = np.clip(static_scores, 0, 1)(0.4, 1.0)

The adversarial benchmark maintains utility by continuously adjusting difficulty upward as models improve, preventing the plateau effect seen in static evaluations. The dynamic benchmark does not optimize for maximum accuracy; it optimizes for maintaining a target difficulty level, keeping scores in the informative 60-85% range where differences between models are statistically detectable.