fix: switch devlogic/sysops totals from geo-mean to token-weighted average

The headline totals were using a per-task geometric mean, which gave
disproportionate weight to short responses with low effective user_tps
(e.g. 2-token SoftBench answers at ~1.2 tok/s). This produced misleadingly
low Devlogic totals (20.5 for asus_210w) when the displayed suite speeds
were all in the 40-50 tok/s range.

Replace geo-mean accumulators (ln_sum/ln_n) with token-weighted
accumulators (rate_token_sum = sum(user_tps × tokens)). The headline total
is now rate_token_sum / total_tokens, matching the methodology already used
by each displayed suite row.

For ScenarioBench, the suite aggregate gen_tps (sum(gen_tokens)/sum(wall))
is weighted by the suite's total generated tokens, keeping the headline
total consistent with the displayed ScenarioBench row.

Results for the 210W bands:
  asus_210w:    devlogic 20.5 → 47.0, sysops 19.5 → 10.6
  sapphire_210w: devlogic 19.6 → 43.7, sysops 8.3 → 8.8
This commit is contained in:
PeterChrz 2026-10-05 00:00:19 -04:00
parent 9b9150dd6b
commit ac941fdd27
Signed by untrusted user who does not match committer: pch
GPG key ID: 8F0826ECF7302C63
2 changed files with 36 additions and 38 deletions

View file

@ -71,17 +71,18 @@ public function handle(): int
'ran_at' => $run->ran_at,
// Devlogic totals: softbench, prosebench, psychbench,
// depthsweep — everything except infrabench and scenariobench.
// Token-weighted: sum(user_tps × tokens) / sum(tokens).
'dl_passed' => 0,
'dl_failed' => 0,
'dl_tokens' => 0,
'dl_ln_sum' => 0.0,
'dl_ln_n' => 0,
'dl_rate_sum' => 0.0,
// Sysops totals: infrabench and scenariobench.
// Token-weighted at the suite level: each suite's aggregate
// rate is weighted by that suite's total tokens.
'so_passed' => 0,
'so_failed' => 0,
'so_tokens' => 0,
'so_ln_sum' => 0.0,
'so_ln_n' => 0,
'so_rate_sum' => 0.0,
'runs' => collect(),
];
@ -105,14 +106,12 @@ public function handle(): int
$sessions[$key]['so_passed'] += $aggs->sum('passed');
$sessions[$key]['so_failed'] += $aggs->sum('failed');
$sessions[$key]['so_tokens'] += $aggs->sum('total_tokens');
$sessions[$key]['so_ln_sum'] += $aggs->sum('ln_sum');
$sessions[$key]['so_ln_n'] += $aggs->sum('ln_n');
$sessions[$key]['so_rate_sum'] += $aggs->sum('rate_token_sum');
} else {
$sessions[$key]['dl_passed'] += $aggs->sum('passed');
$sessions[$key]['dl_failed'] += $aggs->sum('failed');
$sessions[$key]['dl_tokens'] += $aggs->sum('total_tokens');
$sessions[$key]['dl_ln_sum'] += $aggs->sum('ln_sum');
$sessions[$key]['dl_ln_n'] += $aggs->sum('ln_n');
$sessions[$key]['dl_rate_sum'] += $aggs->sum('rate_token_sum');
}
}
@ -127,8 +126,7 @@ public function handle(): int
$sessions[$key]['so_passed'] += $agg['passed'];
$sessions[$key]['so_failed'] += $agg['failed'];
$sessions[$key]['so_tokens'] += $agg['total_tokens'];
$sessions[$key]['so_ln_sum'] += $agg['ln_sum'] ?? 0;
$sessions[$key]['so_ln_n'] += $agg['ln_n'] ?? 0;
$sessions[$key]['so_rate_sum'] += $agg['rate_token_sum'] ?? 0;
$sessions[$key]['runs'] = $band['runs']->merge([$agg]);
}
@ -152,14 +150,14 @@ public function handle(): int
'devlogic_passed' => $band['dl_passed'],
'devlogic_failed' => $band['dl_failed'],
'devlogic_tokens' => $band['dl_tokens'],
'devlogic_tps' => $band['dl_ln_n'] > 0
? round(exp($band['dl_ln_sum'] / $band['dl_ln_n']), 1)
'devlogic_tps' => $band['dl_tokens'] > 0
? round($band['dl_rate_sum'] / $band['dl_tokens'], 1)
: null,
'sysops_passed' => $band['so_passed'],
'sysops_failed' => $band['so_failed'],
'sysops_tokens' => $band['so_tokens'],
'sysops_tps' => $band['so_ln_n'] > 0
? round(exp($band['so_ln_sum'] / $band['so_ln_n']), 1)
'sysops_tps' => $band['so_tokens'] > 0
? round($band['so_rate_sum'] / $band['so_tokens'], 1)
: null,
'sequence' => $bandIndex,
'created_at' => now(),
@ -226,8 +224,7 @@ private function aggregateRun(object $run)
DB::raw('avg(nullif(wall, 0)) as avg_wall'),
DB::raw('sum(total) as total_tokens'),
DB::raw('sum(user_tps * total) / nullif(sum(total), 0) as weighted_user_tps'),
DB::raw('sum(ln(user_tps)) filter (where user_tps > 0) as ln_sum'),
DB::raw('count(*) filter (where user_tps > 0) as ln_n'),
DB::raw('sum(user_tps * total) as rate_token_sum'),
]))
->sortBy(fn ($agg) => $levelOrder[$agg->level] ?? 99)
->values()
@ -244,8 +241,7 @@ private function aggregateRun(object $run)
'total_tokens' => (int) ($agg->total_tokens ?? 0),
'avg_ttft' => $this->roundOrNull($agg->avg_ttft),
'avg_wall' => $this->roundOrNull($agg->avg_wall),
'ln_sum' => (float) ($agg->ln_sum ?? 0),
'ln_n' => (int) ($agg->ln_n ?? 0),
'rate_token_sum' => (float) ($agg->rate_token_sum ?? 0),
]);
}
@ -257,6 +253,9 @@ private function aggregateRun(object $run)
->selectRaw('sum(gen_tokens) / nullif(sum(wall), 0) as gen_tps')
->first();
$genTps = (float) ($agg->gen_tps ?? 0);
$tokens = (int) ($agg->total_tokens ?? 0);
return collect([[
'run_pk' => $run->id,
'run_id' => $run->run_id,
@ -267,7 +266,8 @@ private function aggregateRun(object $run)
'avg_user_tps' => null,
'weighted_user_tps' => null,
'gen_tps' => $this->roundOrNull($agg->gen_tps),
'total_tokens' => (int) ($agg->total_tokens ?? 0),
'total_tokens' => $tokens,
'rate_token_sum' => $genTps * $tokens,
]]);
}
@ -277,8 +277,7 @@ private function aggregateRun(object $run)
->selectRaw('avg(user_tps) as avg_user_tps')
->selectRaw('sum(user_tps * total) / nullif(sum(total), 0) as weighted_user_tps')
->selectRaw('sum(total) as total_tokens')
->selectRaw('sum(ln(user_tps)) filter (where user_tps > 0) as ln_sum')
->selectRaw('count(*) filter (where user_tps > 0) as ln_n')
->selectRaw('sum(user_tps * total) as rate_token_sum')
->first();
return collect([[
@ -292,8 +291,7 @@ private function aggregateRun(object $run)
'weighted_user_tps' => $this->roundOrNull($agg->weighted_user_tps),
'gen_tps' => null,
'total_tokens' => (int) ($agg->total_tokens ?? 0),
'ln_sum' => (float) ($agg->ln_sum ?? 0),
'ln_n' => (int) ($agg->ln_n ?? 0),
'rate_token_sum' => (float) ($agg->rate_token_sum ?? 0),
]]);
}
@ -315,10 +313,11 @@ private function aggregateScenarioBand(array $runs): array
->selectRaw('count(*) filter (where not ok) as failed')
->selectRaw('sum(gen_tokens) as total_tokens')
->selectRaw('sum(gen_tokens) / nullif(sum(wall), 0) as gen_tps')
->selectRaw('sum(ln(gen_tokens / nullif(wall, 0))) filter (where gen_tokens > 0 and wall > 0) as ln_sum')
->selectRaw('count(*) filter (where gen_tokens > 0 and wall > 0) as ln_n')
->first();
$genTps = (float) ($agg->gen_tps ?? 0);
$tokens = (int) ($agg->total_tokens ?? 0);
// Thinking level: keep it only when every scenario row in the band
// agrees — matches the per-run runLevel() rule, applied at band scope.
$level = DB::table('scenario_runs')
@ -335,9 +334,8 @@ private function aggregateScenarioBand(array $runs): array
'avg_user_tps' => null,
'weighted_user_tps' => null,
'gen_tps' => $this->roundOrNull($agg->gen_tps),
'total_tokens' => (int) ($agg->total_tokens ?? 0),
'ln_sum' => (float) ($agg->ln_sum ?? 0),
'ln_n' => (int) ($agg->ln_n ?? 0),
'total_tokens' => $tokens,
'rate_token_sum' => $genTps * $tokens,
];
}
@ -354,8 +352,7 @@ private function emptyAgg(object $run): array
'weighted_user_tps' => null,
'gen_tps' => null,
'total_tokens' => 0,
'ln_sum' => 0.0,
'ln_n' => 0,
'rate_token_sum' => 0.0,
];
}

View file

@ -89,23 +89,24 @@
$band = DB::table('session_summaries')->where('label', 'dual')->first();
// Devlogic: softbench only — 2 observations (50, 10).
// geo-mean = exp((ln(50)+ln(10))/2) = sqrt(500) ≈ 22.4
// Devlogic: softbench only — 2 tasks: user_tps=50 (100 tokens),
// user_tps=10 (200 tokens).
// Token-weighted: (50×100 + 10×200) / (100+200) = 7000/300 ≈ 23.3
expect($band->devlogic_passed)->toBe(1)
->and($band->devlogic_failed)->toBe(1)
->and($band->devlogic_tokens)->toBe(300)
->and($band->devlogic_tps)->toBe(22.4);
->and($band->devlogic_tps)->toBe(23.3);
// Sysops: infrabench (1 obs: user_tps=30, tokens=150) +
// scenariobench (2 obs: gen_tokens/wall = 50.0 and 15.0, tokens=8000).
// Sysops: infrabench (1 task: user_tps=30, tokens=150 → rate_sum=4500)
// + scenariobench (gen_tps = 8000/300 = 26.67, tokens=8000 →
// rate_sum = 26.67×8000 ≈ 213333.33).
// Total sysops tokens = 150 + 8000 = 8150.
// Token-weighted: (4500 + 213333.33) / 8150 ≈ 26.7
expect($band->sysops_passed)->toBe(2)
->and($band->sysops_failed)->toBe(1)
->and($band->sysops_tokens)->toBe(8150);
// sysops_tps is a geo-mean of 3 positive values — just verify it is
// populated and roughly in range.
expect($band->sysops_tps)->not->toBeNull();
expect($band->sysops_tps)->toBeGreaterThan(10.0)->toBeLessThan(50.0);
expect($band->sysops_tps)->toBeGreaterThan(20.0)->toBeLessThan(30.0);
});
test('private runs form their own band and are hidden from guests', function () {