Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@
"y_measuredJPerOutputToken_label": "Measured J per Output Token (J/tok)",
"y_measuredJPerOutputToken_title": "Measured Joules per Output Token",
"y_measuredJPerOutputToken_roofline": "lower_right",
"y_measuredJPerTotalToken": "measuredJPerTotalToken.y",
"y_measuredJPerTotalToken_label": "Measured J per Token (J/tok)",
"y_measuredJPerTotalToken_title": "Measured Joules per Token (input + output)",
"y_measuredJPerTotalToken_roofline": "lower_right",
"y_cost_limit": 5,
"y_latency_limit": 60
},
Expand Down Expand Up @@ -193,6 +197,10 @@
"y_measuredJPerOutputToken_label": "Measured J per Output Token (J/tok)",
"y_measuredJPerOutputToken_title": "Measured Joules per Output Token",
"y_measuredJPerOutputToken_roofline": "lower_left",
"y_measuredJPerTotalToken": "measuredJPerTotalToken.y",
"y_measuredJPerTotalToken_label": "Measured J per Token (J/tok)",
"y_measuredJPerTotalToken_title": "Measured Joules per Token (input + output)",
"y_measuredJPerTotalToken_roofline": "lower_left",
"y_cost_limit": 5,
"y_latency_limit": 60
}
Expand Down
11 changes: 9 additions & 2 deletions packages/app/src/components/inference/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,10 @@ export interface AggDataEntry {
std_e2el: number;
p99_e2el: number;
// Measured GPU telemetry (emitted by runner's aggregate_power.py).
// Optional because historical runs predate the field.
// Optional because historical runs predate the fields.
avg_power_w?: number;
joules_per_output_token?: number;
joules_per_total_token?: number;
disagg: boolean;
num_prefill_gpu: number;
num_decode_gpu: number;
Expand Down Expand Up @@ -162,6 +163,7 @@ export interface InferenceData extends Partial<Omit<AggDataEntry, AggDataConflic
// emit these fields.
measuredAvgPower?: { y: number; roof: boolean };
measuredJPerOutputToken?: { y: number; roof: boolean };
measuredJPerTotalToken?: { y: number; roof: boolean };
}

/**
Expand Down Expand Up @@ -189,7 +191,8 @@ export type YAxisMetricKey =
| 'jOutput'
| 'jInput'
| 'measuredAvgPower'
| 'measuredJPerOutputToken';
| 'measuredJPerOutputToken'
| 'measuredJPerTotalToken';

/**
* Defines the configuration and labels for a specific chart.
Expand Down Expand Up @@ -302,6 +305,10 @@ export interface ChartDefinition {
y_measuredJPerOutputToken_label?: string;
y_measuredJPerOutputToken_title?: string;
y_measuredJPerOutputToken_roofline?: 'upper_right' | 'upper_left' | 'lower_left' | 'lower_right';
y_measuredJPerTotalToken?: string;
y_measuredJPerTotalToken_label?: string;
y_measuredJPerTotalToken_title?: string;
y_measuredJPerTotalToken_roofline?: 'upper_right' | 'upper_left' | 'lower_left' | 'lower_right';
y_cost_limit?: number;
y_latency_limit?: number;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const METRIC_GROUPS: { label: string; metrics: string[]; gated?: boolean }[] = [
{ label: 'All-in Provisioned Energy per Token', metrics: ['y_jTotal', 'y_jOutput', 'y_jInput'] },
{
label: 'Measured Energy',
metrics: ['y_measuredAvgPower', 'y_measuredJPerOutputToken'],
metrics: ['y_measuredAvgPower', 'y_measuredJPerOutputToken', 'y_measuredJPerTotalToken'],
gated: true,
},
{ label: 'Custom User Values', metrics: ['y_costUser', 'y_powerUser'] },
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/lib/benchmark-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export function rowToAggDataEntry(row: BenchmarkRow): AggDataEntry {
// "no measurement" from "0 W" via createChartDataPoint's typeof guard.
avg_power_w: m.avg_power_w,
joules_per_output_token: m.joules_per_output_token,
joules_per_total_token: m.joules_per_total_token,
disagg: row.disagg,
num_prefill_gpu: row.num_prefill_gpu,
num_decode_gpu: row.num_decode_gpu,
Expand Down
27 changes: 27 additions & 0 deletions packages/app/src/lib/chart-utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1265,6 +1265,33 @@ describe('createChartDataPoint measured power fields', () => {
expect(point.measuredAvgPower).toBeDefined();
expect(point.measuredAvgPower!.y).toBe(0);
});

it('emits measuredJPerTotalToken when joules_per_total_token is present', () => {
const e = entry({ joules_per_total_token: 0.93 });
const point = createChartDataPoint('2025-01-01', e, 'median_e2el', 'tput_per_gpu', 'h100');
expect(point.measuredJPerTotalToken).toBeDefined();
expect(point.measuredJPerTotalToken!.y).toBe(0.93);
expect(point.measuredJPerTotalToken!.roof).toBe(false);
});

it('emits J/output and J/total independently — different denominators', () => {
// 8k1k workload: J/output ≈ 9 × J/total (input is ~8x output, so output/total ≈ 1/9).
const e = entry({ joules_per_output_token: 2.04, joules_per_total_token: 0.23 });
const point = createChartDataPoint('2025-01-01', e, 'median_e2el', 'tput_per_gpu', 'h100');
expect(point.measuredJPerOutputToken!.y).toBe(2.04);
expect(point.measuredJPerTotalToken!.y).toBe(0.23);
});

it('omits measuredJPerTotalToken on rows that predate the field', () => {
// Rows ingested before joules_per_total_token was added still have avg_power_w
// and joules_per_output_token. The new field must be absent (not 0) so the
// chart correctly drops them from the J/total view rather than plotting fake data.
const e = entry({ avg_power_w: 458, joules_per_output_token: 2.04 });
const point = createChartDataPoint('2025-01-01', e, 'median_e2el', 'tput_per_gpu', 'h100');
expect(point.measuredAvgPower).toBeDefined();
expect(point.measuredJPerOutputToken).toBeDefined();
expect(point.measuredJPerTotalToken).toBeUndefined();
});
});

// ===========================================================================
Expand Down
13 changes: 11 additions & 2 deletions packages/app/src/lib/chart-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const Y_AXIS_METRICS = [
// distinct from the spec-sheet TDP-derived jTotal/jOutput/jInput above).
'y_measuredAvgPower',
'y_measuredJPerOutputToken',
'y_measuredJPerTotalToken',
] as const;

export type YAxisMetric = (typeof Y_AXIS_METRICS)[number];
Expand Down Expand Up @@ -403,6 +404,9 @@ export function createChartDataPoint(
...(typeof entry.joules_per_output_token === 'number'
? { measuredJPerOutputToken: { y: entry.joules_per_output_token, roof: false } }
: {}),
...(typeof entry.joules_per_total_token === 'number'
? { measuredJPerTotalToken: { y: entry.joules_per_total_token, roof: false } }
: {}),
};
}

Expand Down Expand Up @@ -565,7 +569,8 @@ export const calculateRoofline = (
| `jOutput.y`
| `jInput.y`
| `measuredAvgPower.y`
| `measuredJPerOutputToken.y`,
| `measuredJPerOutputToken.y`
| `measuredJPerTotalToken.y`,
rooflineDirection: 'upper_right' | 'upper_left' | 'lower_left' | 'lower_right',
): InferenceData[] => {
const pointsForRoofline = points.map((p) => {
Expand Down Expand Up @@ -637,7 +642,8 @@ export function computeAllRooflines(
| `jOutput.y`
| `jInput.y`
| `measuredAvgPower.y`
| `measuredJPerOutputToken.y`,
| `measuredJPerOutputToken.y`
| `measuredJPerTotalToken.y`,
rooflineDirection,
);
}
Expand Down Expand Up @@ -683,6 +689,7 @@ export function markRooflinePoints(
if (newPoint.jInput) newPoint.jInput.roof = false;
if (newPoint.measuredAvgPower) newPoint.measuredAvgPower.roof = false;
if (newPoint.measuredJPerOutputToken) newPoint.measuredJPerOutputToken.roof = false;
if (newPoint.measuredJPerTotalToken) newPoint.measuredJPerTotalToken.roof = false;

for (const chartDefYKey of Y_AXIS_METRICS) {
const rooflinePoints = computedRooflines[hwKey]?.[chartDefYKey];
Expand Down Expand Up @@ -749,6 +756,8 @@ export function markRooflinePoints(
newPoint.measuredJPerOutputToken
) {
newPoint.measuredJPerOutputToken.roof = onCurrentRoofline;
} else if (chartDefYKey === 'y_measuredJPerTotalToken' && newPoint.measuredJPerTotalToken) {
newPoint.measuredJPerTotalToken.roof = onCurrentRoofline;
}
}
finalProcessedData.push(newPoint);
Expand Down
5 changes: 4 additions & 1 deletion packages/constants/src/metric-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ export const METRIC_KEYS = new Set([
'p99.9_intvty',
'std_intvty',
// measured power / energy (emitted by runner's aggregate_power.py)
// avg_power_w: mean per-GPU draw (W) during the load window
// avg_power_w: mean per-GPU draw (W) during the load window
// joules_per_output_token: avg_power_w * num_gpus * duration / total_output_tokens
// joules_per_total_token: avg_power_w * num_gpus * duration / (total_input + total_output)
// — workload-shape-fair view that doesn't treat prompt as free
'avg_power_w',
'joules_per_output_token',
'joules_per_total_token',
]);
Loading