diff --git a/docs/ubenchx-huawei-bandwidth.md b/docs/ubenchx-huawei-bandwidth.md new file mode 100644 index 000000000..956cc06e3 --- /dev/null +++ b/docs/ubenchx-huawei-bandwidth.md @@ -0,0 +1,13 @@ +# Huawei copy-bandwidth evidence + +The displayed series is **Huawei 950PR**, measured on Ascend950PR_9579 (28 Cube, 56 Vector, 128 GiB). The measured SKU remains in API metadata. This series does not change the global GPU specification catalog or inference-data normalization. + +`huawei-bandwidth.json` preserves all 42 full-precision plotted points, source input hashes, per-process medians, observed p10/p90 ranges, pool geometry and sample counts. Each size has three processes × six blocks × twenty observations. Aggregation is the pooled median amortized time, converted by `2 * message bytes / time`; bounds invert p90/p10 latency and are not confidence intervals. No observations are smoothed or supplied for missing sizes. The source is the validated October 7 rotating-pool 950PR experiment; the initial scratch-fill curves and overlap-rejected runs are excluded. + +The experiment uses FP16 `copy_` operations in complete rotating-pool NPU graph replays. Each direction has at least 512 MiB and two slots. Every block is checked against source bits before and after timing. The time is amortized over a complete rotation, not an isolated-copy latency. Unlike the GPU series, it is not `triton.testing.do_bench` on one FP32 buffer pair. The UI states this above the chart and labels Huawei tooltip times as amortized. No physical HBM-traffic counters prove that every access misses cache. + +The endpoint is about 1,447 GB/s at 16 GiB. MBU uses an assumed nominal reference of 1,600 GB/s; the exact board ceiling was not independently measured. + +UI/API coverage: the existing bandwidth/MBU/latency selector is presentation-only. `Huawei 950PR` is available through `test=mem-bw`, `gpu=` and `gpu=all`; `test=all` returns empty sections for tests unavailable on an explicitly selected device. API rows retain optional uncertainty/pool fields, `metadata.protocol`, `dtype`, CANN/torch_npu versions and `missingSizesBytes`. `peakBandwidthSource` states the per-run reference qualification. The dashboard and API share `transformUbenchxRun`. + +Hover rulers use the currently selected metric and the same scaled coordinates as the displayed point, including after zoom. diff --git a/packages/app/cypress/e2e/ubenchx.cy.ts b/packages/app/cypress/e2e/ubenchx.cy.ts index 9a6b5103e..eae5264af 100644 --- a/packages/app/cypress/e2e/ubenchx.cy.ts +++ b/packages/app/cypress/e2e/ubenchx.cy.ts @@ -125,3 +125,93 @@ describe('ubenchX hub and test routes', () => { cy.get('meta[name="robots"]').should('have.attr', 'content').and('contain', 'noindex'); }); }); + +const dismiss = { + onBeforeLoad(win: Cypress.AUTWindow) { + win.localStorage.setItem('inferencex-star-modal-dismissed', String(Date.now())); + }, +}; + +describe('Huawei copy-bandwidth evidence', () => { + it('renders Huawei 950PR and qualifies the graph timing in English and Chinese', () => { + cy.visit('/ubenchx/mem-bw', dismiss); + cy.contains('Huawei 950PR').should('be.visible'); + cy.contains('910B3').should('not.exist'); + cy.contains('Graph time is amortized per copy').should('be.visible'); + cy.get('[data-testid="ubenchx-metric-select"]').click(); + cy.contains('[role="option"]', 'Bandwidth (TB/s)').click(); + cy.contains('h2', 'Bandwidth vs Message Size').should('be.visible'); + cy.screenshot('huawei-bandwidth-en', { capture: 'fullPage' }); + cy.visit('/zh/ubenchx/mem-bw', dismiss); + cy.contains('图执行时间按拷贝次数摊销').should('be.visible'); + cy.contains('910B3').should('not.exist'); + cy.screenshot('huawei-bandwidth-zh', { capture: 'fullPage' }); + }); + it('serves measured endpoints and empty unavailable tests through the public API', () => { + cy.request('/api/v1/views/ubenchx?test=all&gpu=Huawei%20950PR').then(({ body }) => { + expect(body.memBw.gpus[0].rows.at(-1).bytes).to.equal(17179869184); + expect(body.memBw.gpus[0].rows.at(-1).mbuPercent).to.be.closeTo(90.4545974, 0.0001); + expect(body.smL2Distance.gpus).to.deep.equal([]); + expect(body.tpcSkyline.gpus).to.deep.equal([]); + }); + }); +}); + +function assertBandwidthRuler(chart: string) { + cy.get(`${chart} circle.point`) + .filter((_, element) => { + const datum = ( + element as unknown as SVGCircleElement & { __data__: { gpuKey: string; bytes: number } } + ).__data__; + return datum.gpuKey === 'H200 SXM' && datum.bytes === 33554432; + }) + .should('have.length', 1) + .then(($point) => { + const x = Number($point.attr('cx')), + y = Number($point.attr('cy')); + cy.wrap($point).trigger('mouseenter', { force: true }); + cy.get(`${chart} .horizontal-ruler`).should(($line) => { + expect(Number($line.attr('y1'))).to.be.closeTo(y, 0.01); + expect(Number($line.attr('y2'))).to.be.closeTo(y, 0.01); + }); + cy.get(`${chart} .vertical-ruler`).should(($line) => { + expect(Number($line.attr('x1'))).to.be.closeTo(x, 0.01); + expect(Number($line.attr('x2'))).to.be.closeTo(x, 0.01); + }); + }); +} + +it('keeps bandwidth hover guides on the rendered point in every metric and after zoom', () => { + cy.visit('/ubenchx/mem-bw', dismiss); + assertBandwidthRuler('#ubenchx-mbu'); + for (const [label, chart] of [ + ['Bandwidth (TB/s)', '#ubenchx-bandwidth'], + ['Latency (ms)', '#ubenchx-latency'], + ]) { + cy.get('[data-testid="ubenchx-metric-select"]').click(); + cy.contains('[role="option"]', label).click(); + assertBandwidthRuler(chart); + } + cy.get('#ubenchx-latency circle.point') + .first() + .then(($point) => { + const previousX = $point.attr('cx'); + cy.get('#ubenchx-latency svg').then(($svg) => { + const rect = $svg[0].getBoundingClientRect(); + cy.wrap($svg).trigger('wheel', { + eventConstructor: 'WheelEvent', + deltaY: -200, + shiftKey: true, + clientX: rect.left + 150, + clientY: rect.top + 100, + force: true, + }); + }); + cy.get('#ubenchx-latency circle.point') + .first() + .should(($current) => { + expect($current.attr('cx')).not.to.equal(previousX); + }); + }); + assertBandwidthRuler('#ubenchx-latency'); +}); diff --git a/packages/app/src/app/api/v1/views/ubenchx/route.test.ts b/packages/app/src/app/api/v1/views/ubenchx/route.test.ts index bd0032b5f..f09e55b33 100644 --- a/packages/app/src/app/api/v1/views/ubenchx/route.test.ts +++ b/packages/app/src/app/api/v1/views/ubenchx/route.test.ts @@ -158,3 +158,47 @@ describe('ubenchx view', () => { expect(h200Last.mbuPercent).toBeCloseTo((4305.39 / 4800) * 100, 4); }); }); + +it('exposes Huawei rotating-copy evidence without inventing missing tests or sizes', async () => { + const response = await GET(request({ test: 'all', gpu: 'huawei 950pr' })); + expect(response.status).toBe(200); + const body = await response.json(); + const run = body.memBw.gpus[0]; + expect(run.gpu).toBe('Huawei 950PR'); + expect(run.peakBandwidthGbps).toBe(1600); + expect(run.peakBandwidthSource).toContain('not independently verified'); + expect(run.metadata).toMatchObject({ + protocol: 'rotating-pool-npu-graph', + dtype: 'float16', + missingSizesBytes: [], + }); + expect(run.rows.some((row: { bytes: number }) => row.bytes === 17179869184)).toBe(true); + const endpoint = run.rows.find((row: { bytes: number }) => row.bytes === 17179869184); + expect(endpoint.bandwidthGbps).toBeCloseTo(1447.2735584561165, 6); + expect(endpoint.mbuPercent).toBeCloseTo(90.45459740350728, 6); + expect(endpoint.observations).toBe(360); + expect(body.smL2Distance.gpus).toEqual([]); + expect(body.tpcSkyline.gpus).toEqual([]); +}); + +it('preserves full-precision Huawei bandwidth arithmetic and API/UI normalization', async () => { + for (const gpu of ['Huawei 950PR']) { + const response = await GET(request({ test: 'mem-bw', gpu })); + const body = await response.json(); + const run = body.gpus[0]; + expect(run).toEqual(transformUbenchxRun(gpu, UBENCHX_RUNS[gpu])); + for (const row of run.rows) { + expect(row.bandwidthGbps).toBeCloseTo((2 * row.bytes) / row.timeMs / 1e6, 6); + expect(row.bandwidthP10Gbps).toBeLessThanOrEqual(row.bandwidthGbps); + expect(row.bandwidthP90Gbps).toBeGreaterThanOrEqual(row.bandwidthGbps); + } + } +}); + +it('does not expose the removed 910B3 bandwidth series', async () => { + const all = await GET(request({ test: 'mem-bw', gpu: 'all' })); + const body = await all.json(); + expect(body.gpus.some((run: { gpu: string }) => run.gpu === 'Ascend 910B3')).toBe(false); + const removed = await GET(request({ test: 'mem-bw', gpu: 'Ascend 910B3' })); + expect(removed.status).toBe(400); +}); diff --git a/packages/app/src/app/api/v1/views/ubenchx/route.ts b/packages/app/src/app/api/v1/views/ubenchx/route.ts index 22b6fff9a..d2c8eaf25 100644 --- a/packages/app/src/app/api/v1/views/ubenchx/route.ts +++ b/packages/app/src/app/api/v1/views/ubenchx/route.ts @@ -64,13 +64,11 @@ export function GET(request: NextRequest) { } // test === 'all': return every test - const smL2GpuKeys = Object.keys(SM_L2_RUNS); - const smL2Gpu = parseEnumParam(s.get('gpu'), 'gpu', ['all', ...smL2GpuKeys], 'all'); const smL2Results = - smL2Gpu === 'all' + rawGpu === null || gpu === 'all' ? Object.entries(SM_L2_RUNS).map(([key, run]) => transformSmL2Run(key, run)) - : SM_L2_RUNS[smL2Gpu] - ? [transformSmL2Run(smL2Gpu, SM_L2_RUNS[smL2Gpu])] + : SM_L2_RUNS[gpu] + ? [transformSmL2Run(gpu, SM_L2_RUNS[gpu])] : []; return Promise.resolve( @@ -85,11 +83,12 @@ export function GET(request: NextRequest) { // Like smL2Distance, default to every GPU rather than mem-bw's first one, and // match the GPU key case-insensitively as parseEnumParam does for the others. tpcSkyline: { - gpus: tpcSkylineResults( - Object.keys(TPC_SKYLINE_RUNS).find( - (key) => key.toLowerCase() === rawGpu?.toLowerCase(), - ) ?? 'all', - ), + gpus: + rawGpu === null || gpu === 'all' + ? tpcSkylineResults('all') + : TPC_SKYLINE_RUNS[gpu] + ? tpcSkylineResults(gpu) + : [], }, }), ); diff --git a/packages/app/src/components/ubenchx/UbenchxContent.tsx b/packages/app/src/components/ubenchx/UbenchxContent.tsx index c6f13faaa..2d373e1bd 100644 --- a/packages/app/src/components/ubenchx/UbenchxContent.tsx +++ b/packages/app/src/components/ubenchx/UbenchxContent.tsx @@ -16,7 +16,12 @@ import { useLocale } from '@/lib/i18n/use-locale'; import type { Locale } from '@/lib/i18n/i18n'; import { UBENCHX_GPU_KEYS, UBENCHX_RUNS } from './ubenchx-data'; -import { transformUbenchxRun, formatBytes, type UbenchxDerivedRow } from './ubenchx-transform'; +import { + transformUbenchxRun, + formatBytes, + sizeAxisTicks, + type UbenchxDerivedRow, +} from './ubenchx-transform'; const GPU_COLORS: Record = { 'B300 SXM': 'oklch(0.62 0.15 128)', @@ -27,13 +32,16 @@ const GPU_COLORS: Record = { MI355X: 'oklch(0.62 0.20 18)', MI325X: 'oklch(0.55 0.20 28)', MI300X: 'oklch(0.48 0.20 38)', + 'Huawei 950PR': '#C7000B', }; const STRINGS = { en: { pageTitle: 'HBM Bandwidth', pageSubtitle: - 'Microbenchmark measuring device-memory copy bandwidth across message sizes on NVIDIA and AMD GPUs.', + 'Microbenchmark measuring device-memory copy bandwidth across message sizes on NVIDIA, AMD and Huawei accelerators.', + protocolNote: + 'Huawei uses amortized rotating-buffer graph timing; NVIDIA/AMD use Triton copy timing. Small-message latency is not directly comparable.', latencyTitle: 'Latency vs Message Size', latencyY: 'Latency (ms)', mbuTitle: 'Memory Bandwidth Utilization (MBU) vs Message Size', @@ -42,10 +50,16 @@ const STRINGS = { bwY: 'Bandwidth (TB/s)', xAxis: 'Message Size', yMetric: 'Y-axis', - peakNote: "MBU is relative to each GPU's own peak HBM bandwidth from GPU_SPECS.", + peakNote: + 'MBU uses each device’s bandwidth reference. Huawei’s 1.6 TB/s references are qualified below.', methodology: 'Methodology', methodologyText: - 'Times b.copy_(a) on float32 tensors using triton.testing.do_bench; bandwidth = 2 * bytes / time (read + write). Power-of-two sizes from 8 B to 16 GiB.', + 'NVIDIA/AMD: times b.copy_(a) on float32 tensors using triton.testing.do_bench; bandwidth = 2 * bytes / time (read + write). Power-of-two sizes from 8 B to 16 GiB.', + huaweiMethodology: + 'Huawei: FP16 rotating source/destination pools of at least 512 MiB each, timed as full rotating-buffer NPU graph replays. Three processes, 360 observations per size; full copied-bit checks before/after each block. Graph time is amortized per copy, not isolated-copy latency. Protocols differ from the GPU curves. No physical HBM-traffic counters were collected.', + huaweiReference: + 'Huawei MBU uses a 1,600 GB/s reference; the exact board bandwidth ceiling was not independently measured.', + amortized: 'Amortized graph copy time', source: 'Source', instructions: 'Shift+Scroll to zoom · Drag to pan · Double-click to reset · Click a point to pin tooltip', @@ -54,12 +68,14 @@ const STRINGS = { tooltipLatency: 'Latency', tooltipBandwidth: 'Bandwidth', tooltipMbu: 'MBU', - tooltipPeak: 'Peak BW', + tooltipPeak: 'Bandwidth reference', aria: 'ubenchX device-memory copy bandwidth charts', }, zh: { pageTitle: 'HBM 带宽', - pageSubtitle: '在 NVIDIA 和 AMD GPU 上测量不同消息大小的显存拷贝带宽微基准测试。', + pageSubtitle: '测量 NVIDIA、AMD 和 Huawei 加速器在不同消息大小下的显存拷贝带宽。', + protocolNote: + 'Huawei 使用轮转缓冲区的图执行摊销计时;NVIDIA/AMD 使用 Triton 拷贝计时。小消息延迟不能直接比较。', latencyTitle: '延迟 vs 消息大小', latencyY: '延迟(ms)', mbuTitle: '显存带宽利用率(MBU)vs 消息大小', @@ -68,10 +84,14 @@ const STRINGS = { bwY: '带宽(TB/s)', xAxis: '消息大小', yMetric: 'Y 轴', - peakNote: 'MBU 基于各 GPU 自身在 GPU_SPECS 中的峰值 HBM 带宽计算。', + peakNote: 'MBU 按各设备的带宽参考值计算。Huawei 的 1.6 TB/s 参考值及其限制见下文。', methodology: '测试方法', methodologyText: - '使用 triton.testing.do_bench 计时 b.copy_(a)(float32 张量);带宽 = 2 * bytes / time(读 + 写)。消息大小为 2 的幂次,从 8 B 到 16 GiB。', + 'NVIDIA/AMD:使用 triton.testing.do_bench 计时 b.copy_(a)(float32 张量);带宽 = 2 * bytes / time(读 + 写)。消息大小为 2 的幂次,从 8 B 到 16 GiB。', + huaweiMethodology: + 'Huawei:源和目标分别使用至少 512 MiB 的 FP16 轮转缓冲池,每次计时覆盖完整缓冲池轮转的一次 NPU 图重放。每个大小在三个进程中共测量 360 次,每个测量块前后逐位校验全部拷贝结果。图执行时间按拷贝次数摊销,不代表单次独立拷贝延迟;与 GPU 曲线的测试协议不同,且未采集物理 HBM 流量计数器。', + huaweiReference: 'Huawei 的 MBU 使用 1,600 GB/s 参考值,该板卡的确切带宽上限未经独立实测。', + amortized: '摊销后的图拷贝时间', source: '数据来源', instructions: 'Shift+滚轮缩放 · 拖动平移 · 双击重置 · 点击数据点固定提示框', dismiss: '点击其他区域关闭', @@ -79,7 +99,7 @@ const STRINGS = { tooltipLatency: '延迟', tooltipBandwidth: '带宽', tooltipMbu: 'MBU', - tooltipPeak: '峰值带宽', + tooltipPeak: '带宽参考值', aria: 'ubenchX 显存拷贝带宽图表', }, } as const; @@ -143,7 +163,7 @@ function formatTooltip(point: ChartPoint, isPinned: boolean, t: (typeof STRINGS) ${point.gpuKey} ${tooltipLine(t.tooltipSize, point.sizeLabel)} - ${tooltipLine(t.tooltipLatency, `${point.timeMs.toFixed(4)} ms`)} + ${tooltipLine(UBENCHX_RUNS[point.gpuKey].metadata.protocol ? t.amortized : t.tooltipLatency, `${point.timeMs.toFixed(4)} ms`)} ${tooltipLine(t.tooltipBandwidth, `${(point.bandwidthGbps / 1000).toFixed(3)} TB/s`)} ${tooltipLine(t.tooltipMbu, `${point.mbuPercent.toFixed(1)}%`)} ${tooltipLine(t.tooltipPeak, `${point.peakBandwidthGbps / 1000} TB/s`)} @@ -215,10 +235,7 @@ function UbenchxChart({ return ticks; }, [yScaleType, yDomain]); - const xTickValues = useMemo(() => { - const all = [...new Set(allPoints.map((p) => p.x))].sort((a, b) => a - b); - return all.filter((_, i) => i % 3 === 0 || i === all.length - 1); - }, [allPoints]); + const xTickValues = useMemo(() => sizeAxisTicks(...xDomain), [xDomain]); return ( @@ -284,6 +301,8 @@ function UbenchxChart({ }} tooltip={{ rulerType: 'crosshair', + getRulerX: (point, scale) => (scale as d3.ScaleLogarithmic)(point.x), + getRulerY: (point, scale) => scale(point.y), attachToLayer: 1, content: (point, isPinned) => formatTooltip(point, isPinned, STRINGS[locale]), }} @@ -324,6 +343,7 @@ export function UbenchxContent() {

{t.pageTitle}

{t.pageSubtitle}

+

{t.protocolNote}

@@ -376,11 +396,16 @@ export function UbenchxContent() {

{t.methodology}

{t.methodologyText}

+

{t.huaweiMethodology}

+

{t.huaweiReference}

{viewResults.map((r) => (

{r.gpu}: {r.metadata.gpu} | Driver: {r.metadata.driver} | PyTorch:{' '} - {r.metadata.torch} | Triton: {r.metadata.triton} | Container: {r.metadata.container} | - Peak: {r.peakBandwidthGbps / 1000} TB/s (GPU_SPECS) + {r.metadata.torch} |{' '} + {r.metadata.cann + ? `CANN: ${r.metadata.cann} | torch_npu: ${r.metadata.torchNpu}` + : `Triton: ${r.metadata.triton} | Container: ${r.metadata.container}`}{' '} + |{r.peakBandwidthGbps / 1000} TB/s

))} {sourceUrls.map((url) => ( diff --git a/packages/app/src/components/ubenchx/data/huawei-bandwidth.json b/packages/app/src/components/ubenchx/data/huawei-bandwidth.json new file mode 100644 index 000000000..8f5eb9d60 --- /dev/null +++ b/packages/app/src/components/ubenchx/data/huawei-bandwidth.json @@ -0,0 +1,567 @@ +{ + "sourceHashes": { + "plot-data.json": "72c246de9aa994fff141715391d6bc651fb08cf2818d363c65cd6465381fae96" + }, + "method": { + "protocol": "rotating-pool-npu-graph", + "dtype": "float16", + "minimumBytesPerPool": 536870912, + "minimumSlots": 2, + "processes": 3, + "blocksPerProcess": 6, + "observationsPerBlock": 20, + "validation": "Full destination-bit comparison before and after every block; graph validation before timing.", + "cacheClaim": "No cache-miss or physical HBM-traffic counters were collected.", + "comparison": "Amortized full-rotation graph timing differs from the Triton FP32 copy protocol of the GPU curves." + }, + "curves": { + "Huawei 950PR": [ + { + "size_bytes": 4096, + "samples": 360, + "slots": 131072, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0005702691851183772, + "effective_GBps": 14.365145818460267, + "GBps_low": 14.327436005139848, + "GBps_high": 14.412772704974659, + "mbu_percent": 0.8978216136537667, + "process_GBps": [14.364048408475348, 14.411478962728552, 14.342808447766135], + "batch_GBps_median": 14.364266091249895 + }, + { + "size_bytes": 8192, + "samples": 360, + "slots": 65536, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0005940047849435359, + "effective_GBps": 27.582269394610023, + "GBps_low": 27.481367193191318, + "GBps_high": 27.84117844615321, + "mbu_percent": 1.7238918371631264, + "process_GBps": [27.488153087043237, 27.828670162745063, 27.582269394610023], + "batch_GBps_median": 27.579877402667684 + }, + { + "size_bytes": 16384, + "samples": 360, + "slots": 32768, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0006229721184354275, + "effective_GBps": 52.59946477588062, + "GBps_low": 52.36497028501048, + "GBps_high": 52.73664160441561, + "mbu_percent": 3.2874665484925387, + "process_GBps": [52.411065473553684, 52.69168746758244, 52.6083175557098], + "batch_GBps_median": 52.603376899676775 + }, + { + "size_bytes": 32768, + "samples": 360, + "slots": 16384, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0006463481695391238, + "effective_GBps": 101.39426873712694, + "GBps_low": 100.85125189230465, + "GBps_high": 102.22767615695236, + "mbu_percent": 6.3371417960704335, + "process_GBps": [102.15424964913102, 101.39426873712694, 100.90658708760559], + "batch_GBps_median": 101.37306610777266 + }, + { + "size_bytes": 65536, + "samples": 360, + "slots": 8192, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0006662394443992525, + "effective_GBps": 196.73407376560763, + "GBps_low": 196.26266400547743, + "GBps_high": 197.16570758024264, + "mbu_percent": 12.295879610350477, + "process_GBps": [196.76399430812592, 196.4309984754961, 196.95375143532294], + "batch_GBps_median": 196.72301540552075 + }, + { + "size_bytes": 131072, + "samples": 360, + "slots": 4096, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0007195061189122498, + "effective_GBps": 364.33880561892875, + "GBps_low": 363.12074483931417, + "GBps_high": 365.1521295743117, + "mbu_percent": 22.771175351183047, + "process_GBps": [363.56513174458007, 364.6284489048512, 364.69990541338376], + "batch_GBps_median": 364.2804842987366 + }, + { + "size_bytes": 262144, + "samples": 360, + "slots": 2048, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.000844078604131937, + "effective_GBps": 621.1364645822122, + "GBps_low": 615.250629012125, + "GBps_high": 628.6672699985434, + "mbu_percent": 38.82102903638826, + "process_GBps": [615.7588569343593, 621.1364645822122, 627.8756728229461], + "batch_GBps_median": 620.9686541056155 + }, + { + "size_bytes": 524288, + "samples": 360, + "slots": 1024, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.0031780170975252986, + "effective_GBps": 329.94662011621, + "GBps_low": 329.2355792227781, + "GBps_high": 330.87846515950685, + "mbu_percent": 20.621663757263125, + "process_GBps": [329.92995378815425, 330.1963693698262, 329.7884847358392], + "batch_GBps_median": 329.96834567930404 + }, + { + "size_bytes": 1048576, + "samples": 360, + "slots": 512, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.003461894579231739, + "effective_GBps": 605.781589243367, + "GBps_low": 604.1844103586747, + "GBps_high": 607.5343065651464, + "mbu_percent": 37.861349327710435, + "process_GBps": [605.3072555017843, 606.7590979679034, 605.5757694870043], + "batch_GBps_median": 605.4209277493137 + }, + { + "size_bytes": 2097152, + "samples": 360, + "slots": 256, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.004387195222079754, + "effective_GBps": 956.0331345391296, + "GBps_low": 952.9973075264429, + "GBps_high": 958.93766395145, + "mbu_percent": 59.7520709086956, + "process_GBps": [955.7395073504729, 957.6851957064066, 955.2080999198523], + "batch_GBps_median": 954.8897926354608 + }, + { + "size_bytes": 3145728, + "samples": 360, + "slots": 171, + "combined_pool_bytes": 1075838976, + "amortized_copy_ms": 0.005587263239754571, + "effective_GBps": 1126.0353647264994, + "GBps_low": 1122.7849712816576, + "GBps_high": 1129.6886350059103, + "mbu_percent": 70.37721029540621, + "process_GBps": [1124.0377115938306, 1128.511718525218, 1126.0442512285124], + "batch_GBps_median": 1125.0290721959668 + }, + { + "size_bytes": 4194304, + "samples": 360, + "slots": 128, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.006938999984413385, + "effective_GBps": 1208.9073380664033, + "GBps_low": 1177.402355803721, + "GBps_high": 1212.7778548647907, + "mbu_percent": 75.55670862915021, + "process_GBps": [1211.097879874776, 1210.4869168626078, 1178.9190655661212], + "batch_GBps_median": 1208.9490890624584 + }, + { + "size_bytes": 6291456, + "samples": 360, + "slots": 86, + "combined_pool_bytes": 1082130432, + "amortized_copy_ms": 0.00984141265237054, + "effective_GBps": 1278.56766548338, + "GBps_low": 1274.1247269310666, + "GBps_high": 1284.158674156476, + "mbu_percent": 79.91047909271126, + "process_GBps": [1282.6617782820608, 1276.198765385342, 1277.6286712390001], + "batch_GBps_median": 1276.3195232299317 + }, + { + "size_bytes": 8388608, + "samples": 360, + "slots": 64, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.012577531393617392, + "effective_GBps": 1333.903726808727, + "GBps_low": 1326.1317625707748, + "GBps_high": 1340.565966742003, + "mbu_percent": 83.36898292554544, + "process_GBps": [1334.1515397527842, 1327.766858133526, 1339.6194438688508], + "batch_GBps_median": 1332.6867850151746 + }, + { + "size_bytes": 10485760, + "samples": 360, + "slots": 52, + "combined_pool_bytes": 1090519040, + "amortized_copy_ms": 0.015623923104542952, + "effective_GBps": 1342.2697909913634, + "GBps_low": 1336.6772622737928, + "GBps_high": 1347.9781150815716, + "mbu_percent": 83.89186193696021, + "process_GBps": [1337.9460335993538, 1342.306966375744, 1346.9433877673446], + "batch_GBps_median": 1340.7311119397755 + }, + { + "size_bytes": 12582912, + "samples": 360, + "slots": 43, + "combined_pool_bytes": 1082130432, + "amortized_copy_ms": 0.018632860377777456, + "effective_GBps": 1350.6151760796806, + "GBps_low": 1344.0398897862465, + "GBps_high": 1358.748497891573, + "mbu_percent": 84.41344850498004, + "process_GBps": [1350.6505949132234, 1346.2757751177407, 1356.9850326410058], + "batch_GBps_median": 1348.8537056521081 + }, + { + "size_bytes": 14680064, + "samples": 360, + "slots": 37, + "combined_pool_bytes": 1086324736, + "amortized_copy_ms": 0.022153202746365522, + "effective_GBps": 1325.3220464845363, + "GBps_low": 1319.9093811900254, + "GBps_high": 1330.0781760726736, + "mbu_percent": 82.83262790528352, + "process_GBps": [1327.1240991973198, 1321.7325829398656, 1326.4005859737204], + "batch_GBps_median": 1324.9484015710134 + }, + { + "size_bytes": 16777216, + "samples": 360, + "slots": 32, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.02452706266194582, + "effective_GBps": 1368.05749887288, + "GBps_low": 1362.4227558956425, + "GBps_high": 1405.1738543571612, + "mbu_percent": 85.503593679555, + "process_GBps": [1403.7070409211979, 1366.4757829674227, 1365.1267696454465], + "batch_GBps_median": 1365.134384116032 + }, + { + "size_bytes": 20971520, + "samples": 360, + "slots": 26, + "combined_pool_bytes": 1090519040, + "amortized_copy_ms": 0.03062876944358532, + "effective_GBps": 1369.4001019941159, + "GBps_low": 1363.9707531361396, + "GBps_high": 1377.9132642784325, + "mbu_percent": 85.58750637463224, + "process_GBps": [1376.3019554998873, 1368.7993794418499, 1365.8001884701316], + "batch_GBps_median": 1367.3373559143379 + }, + { + "size_bytes": 25165824, + "samples": 360, + "slots": 22, + "combined_pool_bytes": 1107296256, + "amortized_copy_ms": 0.036556841297583145, + "effective_GBps": 1376.8051673361488, + "GBps_low": 1371.858982254753, + "GBps_high": 1382.3759137486902, + "mbu_percent": 86.0503229585093, + "process_GBps": [1380.4311165869726, 1374.072416844146, 1376.8590964457214], + "batch_GBps_median": 1375.348029222982 + }, + { + "size_bytes": 29360128, + "samples": 360, + "slots": 19, + "combined_pool_bytes": 1115684864, + "amortized_copy_ms": 0.04246739337318822, + "effective_GBps": 1382.713920866003, + "GBps_low": 1377.669562096928, + "GBps_high": 1387.3488780220366, + "mbu_percent": 86.41962005412519, + "process_GBps": [1381.8011213782559, 1385.1437943122432, 1381.5991771896959], + "batch_GBps_median": 1380.5958957194011 + }, + { + "size_bytes": 33554432, + "samples": 360, + "slots": 16, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.04832074977457523, + "effective_GBps": 1388.8208339703876, + "GBps_low": 1384.4338520112901, + "GBps_high": 1392.2640006079937, + "mbu_percent": 86.80130212314923, + "process_GBps": [1387.8192098065958, 1389.010375863181, 1389.4390705018664], + "batch_GBps_median": 1387.0774159404414 + }, + { + "size_bytes": 41943040, + "samples": 360, + "slots": 13, + "combined_pool_bytes": 1090519040, + "amortized_copy_ms": 0.06041388557507442, + "effective_GBps": 1388.5231714778124, + "GBps_low": 1383.8758414401218, + "GBps_high": 1393.3063584376398, + "mbu_percent": 86.78269821736328, + "process_GBps": [1390.0089669108295, 1389.6370221270724, 1385.8331729059744], + "batch_GBps_median": 1387.8124588484288 + }, + { + "size_bytes": 50331648, + "samples": 360, + "slots": 11, + "combined_pool_bytes": 1107296256, + "amortized_copy_ms": 0.07186286286874251, + "effective_GBps": 1400.76935403842, + "GBps_low": 1396.7396948436487, + "GBps_high": 1404.5265250412012, + "mbu_percent": 87.54808462740125, + "process_GBps": [1400.346839807555, 1400.3176010977477, 1402.0889550831864], + "batch_GBps_median": 1399.556664039117 + }, + { + "size_bytes": 58720256, + "samples": 360, + "slots": 10, + "combined_pool_bytes": 1174405120, + "amortized_copy_ms": 0.08327209949493408, + "effective_GBps": 1410.322457489433, + "GBps_low": 1405.7274212201607, + "GBps_high": 1413.7738360010965, + "mbu_percent": 88.14515359308956, + "process_GBps": [1409.3188375584293, 1411.0671928396293, 1410.7502546894375], + "batch_GBps_median": 1408.781172319667 + }, + { + "size_bytes": 67108864, + "samples": 360, + "slots": 8, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.09513581544160843, + "effective_GBps": 1410.8012568870963, + "GBps_low": 1406.9945003916703, + "GBps_high": 1414.1961037821745, + "mbu_percent": 88.17507855544352, + "process_GBps": [1411.050670550063, 1411.695333004461, 1409.8713471535327], + "batch_GBps_median": 1409.2369701670573 + }, + { + "size_bytes": 83886080, + "samples": 360, + "slots": 7, + "combined_pool_bytes": 1174405120, + "amortized_copy_ms": 0.11860078573226929, + "effective_GBps": 1414.5956872387903, + "GBps_low": 1410.6375269373686, + "GBps_high": 1417.3131440614306, + "mbu_percent": 88.4122304524244, + "process_GBps": [1414.6621620153617, 1414.8035623006328, 1414.3324901912774], + "batch_GBps_median": 1412.7804242883767 + }, + { + "size_bytes": 100663296, + "samples": 360, + "slots": 6, + "combined_pool_bytes": 1207959552, + "amortized_copy_ms": 0.14194091657797497, + "effective_GBps": 1418.3830628527865, + "GBps_low": 1415.0715362540511, + "GBps_high": 1421.4749376709563, + "mbu_percent": 88.64894142829915, + "process_GBps": [1417.197439640835, 1419.9669831246383, 1418.5671319453127], + "batch_GBps_median": 1417.1970038008044 + }, + { + "size_bytes": 117440512, + "samples": 360, + "slots": 5, + "combined_pool_bytes": 1174405120, + "amortized_copy_ms": 0.16549269556999208, + "effective_GBps": 1419.2833296419503, + "GBps_low": 1415.7046164879473, + "GBps_high": 1422.7888803064116, + "mbu_percent": 88.7052081026219, + "process_GBps": [1418.035726791431, 1421.5406888253556, 1419.221531165787], + "batch_GBps_median": 1417.373295444885 + }, + { + "size_bytes": 134217728, + "samples": 360, + "slots": 4, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.18802624940872192, + "effective_GBps": 1427.6488354372725, + "GBps_low": 1424.3697850269964, + "GBps_high": 1430.5182208391896, + "mbu_percent": 89.22805221482953, + "process_GBps": [1426.9003991044156, 1428.8447267430195, 1427.4391013847599], + "batch_GBps_median": 1425.8724737670598 + }, + { + "size_bytes": 167772160, + "samples": 360, + "slots": 4, + "combined_pool_bytes": 1342177280, + "amortized_copy_ms": 0.23519062995910645, + "effective_GBps": 1426.6908509847626, + "GBps_low": 1423.1268520607425, + "GBps_high": 1429.481720077409, + "mbu_percent": 89.16817818654766, + "process_GBps": [1427.211970331157, 1427.1839288892766, 1425.7724723159786], + "batch_GBps_median": 1425.6267553058578 + }, + { + "size_bytes": 201326592, + "samples": 360, + "slots": 3, + "combined_pool_bytes": 1207959552, + "amortized_copy_ms": 0.28174516558647156, + "effective_GBps": 1429.1396381614934, + "GBps_low": 1425.8124605395335, + "GBps_high": 1431.8558297908203, + "mbu_percent": 89.32122738509334, + "process_GBps": [1428.6841038184275, 1430.32671050888, 1428.4932214139817], + "batch_GBps_median": 1427.274822290363 + }, + { + "size_bytes": 234881024, + "samples": 360, + "slots": 3, + "combined_pool_bytes": 1409286144, + "amortized_copy_ms": 0.3278158406416575, + "effective_GBps": 1433.0059434605143, + "GBps_low": 1429.67433105145, + "GBps_high": 1435.7928738333292, + "mbu_percent": 89.56287146628215, + "process_GBps": [1432.3978508665973, 1432.8070813174613, 1434.1047609854077], + "batch_GBps_median": 1431.3857785295954 + }, + { + "size_bytes": 268435456, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 1073741824, + "amortized_copy_ms": 0.3740215003490448, + "effective_GBps": 1435.4012042061236, + "GBps_low": 1431.904196702147, + "GBps_high": 1437.892052940043, + "mbu_percent": 89.71257526288272, + "process_GBps": [1435.0981202520124, 1434.1569237785213, 1436.775462699177], + "batch_GBps_median": 1433.2310041203525 + }, + { + "size_bytes": 402653184, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 1610612736, + "amortized_copy_ms": 0.5620547533035278, + "effective_GBps": 1432.789889715795, + "GBps_low": 1428.8349576419362, + "GBps_high": 1436.227466579229, + "mbu_percent": 89.54936810723719, + "process_GBps": [1430.7731666693924, 1432.4834079629481, 1435.277805401062], + "batch_GBps_median": 1431.1821217661327 + }, + { + "size_bytes": 536870912, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 2147483648, + "amortized_copy_ms": 0.7447257339954376, + "effective_GBps": 1441.7949789909878, + "GBps_low": 1439.3552545002076, + "GBps_high": 1443.74480021992, + "mbu_percent": 90.11218618693674, + "process_GBps": [1442.0147246892238, 1441.567974938845, 1441.7049764557262], + "batch_GBps_median": 1440.8970842451374 + }, + { + "size_bytes": 805306368, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 3221225472, + "amortized_copy_ms": 1.1200995445251465, + "effective_GBps": 1437.9192848282078, + "GBps_low": 1436.1065741200653, + "GBps_high": 1439.9701529328927, + "mbu_percent": 89.86995530176299, + "process_GBps": [1437.5820769450886, 1438.772266531497, 1437.6411992869985], + "batch_GBps_median": 1437.3523678865686 + }, + { + "size_bytes": 1073741824, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 4294967296, + "amortized_copy_ms": 1.4882272481918335, + "effective_GBps": 1442.9810034785683, + "GBps_low": 1441.2330394734774, + "GBps_high": 1444.5463566820863, + "mbu_percent": 90.18631271741052, + "process_GBps": [1443.3638069253957, 1443.1904160850872, 1442.4622134332415], + "batch_GBps_median": 1442.7277551709155 + }, + { + "size_bytes": 2147483648, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 8589934592, + "amortized_copy_ms": 2.967597246170044, + "effective_GBps": 1447.2878021244455, + "GBps_low": 1445.3349460340112, + "GBps_high": 1448.8148362854529, + "mbu_percent": 90.45548763277785, + "process_GBps": [1447.7066912988864, 1446.9365013812408, 1446.9506221940387], + "batch_GBps_median": 1446.8216162553915 + }, + { + "size_bytes": 4294967296, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 17179869184, + "amortized_copy_ms": 5.950134038925171, + "effective_GBps": 1443.653964062914, + "GBps_low": 1441.012436867068, + "GBps_high": 1445.7000531256922, + "mbu_percent": 90.22837275393212, + "process_GBps": [1443.8215067244923, 1444.6956034982918, 1441.9575889809778], + "batch_GBps_median": 1443.6855618225197 + }, + { + "size_bytes": 8589934592, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 34359738368, + "amortized_copy_ms": 11.879819393157959, + "effective_GBps": 1446.1389197460815, + "GBps_low": 1444.9356122718234, + "GBps_high": 1447.2923369021803, + "mbu_percent": 90.38368248413009, + "process_GBps": [1445.894414437915, 1446.0273644616723, 1446.644442512111], + "batch_GBps_median": 1445.9562499800982 + }, + { + "size_bytes": 17179869184, + "samples": 360, + "slots": 2, + "combined_pool_bytes": 68719476736, + "amortized_copy_ms": 23.74101161956787, + "effective_GBps": 1447.2735584561165, + "GBps_low": 1446.4640837505524, + "GBps_high": 1448.0364484174981, + "mbu_percent": 90.45459740350728, + "process_GBps": [1447.3357675590314, 1447.4756703662817, 1447.1418323453292], + "batch_GBps_median": 1447.2186686979203 + } + ] + } +} diff --git a/packages/app/src/components/ubenchx/ubenchx-data.ts b/packages/app/src/components/ubenchx/ubenchx-data.ts index deb4343a8..1816139cf 100644 --- a/packages/app/src/components/ubenchx/ubenchx-data.ts +++ b/packages/app/src/components/ubenchx/ubenchx-data.ts @@ -1,7 +1,15 @@ +import huawei from './data/huawei-bandwidth.json'; + export interface UbenchxRow { readonly bytes: number; readonly timeMs: number; readonly bandwidthGbps: number; + readonly observations?: number; + readonly bandwidthP10Gbps?: number; + readonly bandwidthP90Gbps?: number; + readonly processBandwidthGbps?: readonly number[]; + readonly slots?: number; + readonly combinedPoolBytes?: number; } export interface UbenchxRunMetadata { @@ -12,11 +20,17 @@ export interface UbenchxRunMetadata { readonly container: string; readonly date: string; readonly sourceUrl: string; + readonly protocol?: 'rotating-pool-npu-graph'; + readonly dtype?: 'float16'; + readonly cann?: string; + readonly torchNpu?: string; + readonly missingSizesBytes?: readonly number[]; } export interface UbenchxRun { readonly metadata: UbenchxRunMetadata; readonly rows: readonly UbenchxRow[]; + readonly bandwidthReference?: { readonly gbps: number; readonly source: string }; } export const UBENCHX_GPU_KEYS = [ @@ -28,6 +42,7 @@ export const UBENCHX_GPU_KEYS = [ 'MI355X', 'MI325X', 'MI300X', + 'Huawei 950PR', ] as const; export type UbenchxGpuKey = (typeof UBENCHX_GPU_KEYS)[number]; @@ -394,3 +409,39 @@ export const UBENCHX_RUNS: Record = { ], }, }; + +for (const [gpu, points] of Object.entries(huawei.curves)) { + UBENCHX_RUNS[gpu] = { + metadata: { + gpu: 'Ascend950PR_9579', + driver: '25.7.rc1.10', + torch: '2.10.0+cpu', + torchNpu: '2.10.0.post4', + cann: '9.1.0', + triton: 'Not used', + container: 'Not recorded', + date: '2026-10-07', + dtype: 'float16', + protocol: 'rotating-pool-npu-graph', + missingSizesBytes: [], + sourceUrl: + 'https://github.com/SemiAnalysisAI/InferenceX-app/blob/0e7525c9b62124a43856d26e1ce2ac5b2ae31b93/packages/app/src/components/ubenchx/data/huawei-bandwidth.json', + }, + bandwidthReference: { + gbps: 1600, + source: + '950PR 128 GiB nominal reference assumed at 1600 GB/s; not independently verified by memory-controller counters', + }, + rows: points.map((point) => ({ + bytes: point.size_bytes, + timeMs: point.amortized_copy_ms, + bandwidthGbps: point.effective_GBps, + observations: point.samples, + bandwidthP10Gbps: point.GBps_low, + bandwidthP90Gbps: point.GBps_high, + processBandwidthGbps: point.process_GBps, + slots: point.slots, + combinedPoolBytes: point.combined_pool_bytes, + })), + }; +} diff --git a/packages/app/src/components/ubenchx/ubenchx-transform.test.ts b/packages/app/src/components/ubenchx/ubenchx-transform.test.ts new file mode 100644 index 000000000..414d7c154 --- /dev/null +++ b/packages/app/src/components/ubenchx/ubenchx-transform.test.ts @@ -0,0 +1,10 @@ +import { expect, it } from 'vitest'; +import { sizeAxisTicks } from './ubenchx-transform'; + +it('spaces binary-size labels independently of dense sample clusters', () => { + expect(sizeAxisTicks(8, 17179869184)).toEqual([ + 8, 256, 8192, 262144, 8388608, 268435456, 17179869184, + ]); + expect(sizeAxisTicks(4096, 4096)).toEqual([4096]); + expect(() => sizeAxisTicks(0, 1024)).toThrow('Invalid'); +}); diff --git a/packages/app/src/components/ubenchx/ubenchx-transform.ts b/packages/app/src/components/ubenchx/ubenchx-transform.ts index f48bdec0a..098fc749b 100644 --- a/packages/app/src/components/ubenchx/ubenchx-transform.ts +++ b/packages/app/src/components/ubenchx/ubenchx-transform.ts @@ -30,7 +30,9 @@ export function formatBytes(bytes: number): string { } export function transformUbenchxRun(gpuKey: string, run: UbenchxRun): UbenchxViewResult { - const peakBandwidthGbps = getPeakBandwidthGbps(gpuKey); + const peakBandwidthGbps = run.bandwidthReference?.gbps ?? getPeakBandwidthGbps(gpuKey); + if (!Number.isFinite(peakBandwidthGbps) || peakBandwidthGbps <= 0) + throw new Error('Bandwidth reference must be finite and positive'); const rows: UbenchxDerivedRow[] = run.rows.map((row) => ({ ...row, mbuPercent: peakBandwidthGbps > 0 ? (row.bandwidthGbps / peakBandwidthGbps) * 100 : 0, @@ -40,8 +42,25 @@ export function transformUbenchxRun(gpuKey: string, run: UbenchxRun): UbenchxVie return { gpu: gpuKey, peakBandwidthGbps, - peakBandwidthSource: `GPU_SPECS ${gpuKey} memoryBandwidth (${peakBandwidthGbps / 1000} TB/s)`, + peakBandwidthSource: + run.bandwidthReference?.source ?? + `GPU_SPECS ${gpuKey} memoryBandwidth (${peakBandwidthGbps / 1000} TB/s)`, metadata: run.metadata, rows, }; } + +export function sizeAxisTicks(min: number, max: number): number[] { + if (!(min > 0 && max >= min) || !Number.isFinite(max)) throw new Error('Invalid size domain'); + if (min === max) return [min]; + const first = Math.ceil(Math.log2(min)), + last = Math.floor(Math.log2(max)); + const step = Math.max(1, Math.ceil((last - first) / 7)); + const ticks = [min]; + for (let exponent = first + step; exponent <= last; exponent += step) ticks.push(2 ** exponent); + if (ticks.at(-1) !== max) { + if (ticks.length > 1 && Math.log2(max / ticks.at(-1)!) < step / 2) ticks.pop(); + ticks.push(max); + } + return ticks; +} diff --git a/packages/app/src/lib/api/api-route-catalog.ts b/packages/app/src/lib/api/api-route-catalog.ts index 96a7d18d8..a8bf17779 100644 --- a/packages/app/src/lib/api/api-route-catalog.ts +++ b/packages/app/src/lib/api/api-route-catalog.ts @@ -518,7 +518,7 @@ export const apiRouteCatalog = [ method: 'GET', classification: 'published-read', operationId: 'get-ubenchx-view', - sourceSha256: '237d7b2f095786cea52f8d65eaed73a4f091fa880803809271e2542b0d62b4d9', + sourceSha256: '7446193b97506c03e3df2908ea8d25f61e134e18005efe8839ff2bb3ed105595', }, { source: 'src/app/api/v1/operatorx/runs/route.ts', @@ -1158,6 +1158,23 @@ export interface ApiContractSourceDigest { * touching a route module. Digest changes require an explicit documentation review. */ export const apiContractSourceDigests = [ + { + source: 'src/components/ubenchx/ubenchx-transform.ts', + sourceSha256: '9458ad2b969525fe68da5a3b54ac151384610d0bd2ba84585a9043b3a67bf949', + reviewArea: { + en: 'ubenchX Huawei measurement units, protocol metadata and shared UI/API calculations.', + zh: 'ubenchX Huawei 测量单位、协议元数据及界面与 API 共用的计算。', + }, + }, + { + source: 'src/components/ubenchx/ubenchx-data.ts', + sourceSha256: '21beff5ed2452035aae42df208734cbda323272539ed265bf8dd39c3043981d1', + reviewArea: { + en: 'ubenchX Huawei measurement units, protocol metadata and shared UI/API calculations.', + zh: 'ubenchX Huawei 测量单位、协议元数据及界面与 API 共用的计算。', + }, + }, + { source: 'src/lib/catalog/gpu-specs.ts', sourceSha256: 'fb9dc719f347fb5b73ea73cf35a19d12c640b34a284f31cd70a8b9b2df76b4fd', diff --git a/packages/app/src/lib/views-api/docs/extensions.ts b/packages/app/src/lib/views-api/docs/extensions.ts index 8866d32e6..59f99a10b 100644 --- a/packages/app/src/lib/views-api/docs/extensions.ts +++ b/packages/app/src/lib/views-api/docs/extensions.ts @@ -357,8 +357,8 @@ const PARAMETER_NOTES: Record = { 'JSON 对象,将视频数据点 ID 映射为 {hourly,source,date} 字符串;hourly 为非负 USD/deployment-hour。', ], gpu: [ - 'GPU key, e.g. "H100 SXM"; default first available GPU. "all" returns every GPU.', - 'GPU 键,如 "H100 SXM";默认首个可用 GPU。设为 "all" 返回全部 GPU。', + 'Accelerator key, e.g. "H100 SXM", "Huawei 950PR". Default: first available device. "all" returns all devices; unavailable tests retain their section object with an empty gpus array for an explicitly selected device.', + '加速器键,如 "H100 SXM"或 "Huawei 950PR"。默认首个可用设备;"all" 返回全部设备。显式选择设备时,没有数据的测试部分保留对象结构,其中 gpus 数组为空。', ], test: [ 'ubenchX test selector: mem-bw, sm-l2-distance, tpc-skyline, or all (default). Determines which benchmark data is returned.', @@ -503,8 +503,8 @@ const NEW_VIEWS = { }, ], ubenchx: [ - 'ubenchX microbenchmarks (mem-bw, sm-l2-distance, and tpc-skyline)', - 'ubenchX 微基准测试(mem-bw、sm-l2-distance 和 tpc-skyline)', + 'ubenchX microbenchmarks with per-device protocols and bandwidth references (mem-bw, sm-l2-distance, and tpc-skyline)', + 'ubenchX 微基准测试及各设备的测试协议和带宽参考值(mem-bw、sm-l2-distance 和 tpc-skyline)', { gpus: objects, tests: strings, memBw: object, smL2Distance: object, tpcSkyline: object }, ], } as const; @@ -519,7 +519,7 @@ export const operations: ApiOperation[] = Object.entries(NEW_VIEWS).map( path: `/api/v1/views/${view}`, summary: text(en, zh), description: text( - `Read-only ${en.toLowerCase()} using the dashboard's source handlers and calculation helpers. Unknown and repeated query keys return 400. The response includes resolved params and preserves missing evidence. Renderer-only styling is not an API parameter.${ + `Read-only ${en.toLowerCase()} using the dashboard's source handlers and calculation helpers. Unknown and repeated query keys return 400. The response includes resolved params and preserves missing evidence. Renderer-only styling is not an API parameter.${view === 'ubenchx' ? ' mem-bw includes Huawei rotating-pool FP16 graph results. timeMs is amortized per copy for metadata.protocol=rotating-pool-npu-graph. peakBandwidthSource documents the per-run normalization reference; optional rows retain observations, bandwidthP10Gbps, bandwidthP90Gbps, processBandwidthGbps, slots and combinedPoolBytes. Missing sizes are listed in metadata.missingSizesBytes.' : ''}${ view === 'video' ? ' Only already published artifacts are read. Cell, phase, slot and GPU-basis choices select result evidence and normalized serving rates; x/y/cost/workload filters produce computed tradeoff points. Local bundles and arbitrary URLs are excluded. Responses are no-store.' : view === 'current-inferencex-image' @@ -528,7 +528,7 @@ export const operations: ApiOperation[] = Object.entries(NEW_VIEWS).map( ? ' Telemetry comes from one source per run: its stored DB series once the run is ingested, otherwise its GitHub artifacts. Stored coverage describes the entire run: complete means the recorded artifact/file inventory is present, partial means recorded telemetry is missing, and unknown means no complete inventory was retained; missingArtifacts names entirely missing artifacts and may be empty when only a file is incomplete. GitHub coverage is null (unassessed). Responses use private, no-store; an upstream database failure remains HTTP 503 rather than an empty dataset. File/host series retain separate identities. Raw rows and charts respect selected GPU indices. Statistics cover every chip of the selected file/host series, including startup and warmup, and are computed from its samples on each read; a stored series whose samples do not match its recorded sample count returns none. Mean is sample-weighted, P50/P95/P99 interpolate linearly at p*(N-1), and standard deviation divides by N. These statistics are not serving-window power or J/token. Line time remains relative to the first sample across all chips; missing metric readings are omitted rather than zero-filled. Correlations require both readings. This endpoint projects the raw GPU-metrics explorer, not the inference Power Timeline. Timeline role/window evidence comes from its existing series=power read plus benchmark power_audit. Timeline i_ptaxis, i_ptlines, i_ptwindow, i_ptfocus, i_ptutility and i_ptconc share fields control client rendering only and are not accepted here; they do not change raw telemetry or full-record statistics.' : '' }`, - `只读${zh},使用仪表板的数据读取和计算函数。未知或重复查询键返回 400;响应包含解析后的参数,保留缺失数据。仅影响样式的控件不作为 API 参数。${ + `只读${zh},使用仪表板的数据读取和计算函数。未知或重复查询键返回 400;响应包含解析后的参数,保留缺失数据。仅影响样式的控件不作为 API 参数。${view === 'ubenchx' ? ' mem-bw 包含 Huawei FP16 轮转缓冲池图执行结果。metadata.protocol=rotating-pool-npu-graph 时,timeMs 为每次拷贝的摊销时间。peakBandwidthSource 说明该次测量采用的归一化参考值;行数据可保留 observations、bandwidthP10Gbps、bandwidthP90Gbps、processBandwidthGbps、slots 和 combinedPoolBytes。缺失大小列在 metadata.missingSizesBytes。' : ''}${ view === 'video' ? ' 仅读取已发布产物。cell、阶段、slot 和 GPU 口径选择对应结果证据,并计算 serving 归一化速率;x/y、成本及工作负载筛选生成权衡图数据点。不读取本地数据包或任意 URL。响应不缓存。' : view === 'current-inferencex-image' diff --git a/packages/skills/skills/inferencex-api/integrity.json b/packages/skills/skills/inferencex-api/integrity.json index 8f9eddf82..bf69dab0f 100644 --- a/packages/skills/skills/inferencex-api/integrity.json +++ b/packages/skills/skills/inferencex-api/integrity.json @@ -9,7 +9,7 @@ "references/cli-contract.md": "4f070877eeefffad2860c58f79f68432fbe27aa21fc578a1c696f0309a3d7d7b", "references/cli.md": "11e1183f7ed70d669019e8435ac16a6a9f873de027507b8850a9943f6659bfd9", "references/collectivex.md": "f79cf8e2779a9289ab69f79f4a4b338e0b9a5f76a3750a3e969f33a744e726a1", - "references/dashboard-views.md": "db9ba48896f75acf6531bd49d26062ee09307c6348f1e10b055cf62e093df85d", + "references/dashboard-views.md": "24c1f4d8bebf776a2ddfda588baa81f584c770c0d71ab8011bfa1ec19b69082e", "references/offline-exports.md": "95aa565dcd4c9e592159baa560a9a58217bc61e395de1c6daf0576795c7f4ec9", "references/pareto.md": "1b4d2d163f982e3f2d97addce310789eae9c1e5badd82dd7501708c9e0385027", "references/powerx.md": "5af5527d54a7e6191a5769080c122638b333b1ceb59e19fdee7df662a5cd3c8f", diff --git a/packages/skills/skills/inferencex-api/references/dashboard-views.md b/packages/skills/skills/inferencex-api/references/dashboard-views.md index 6515340c6..904d52d0f 100644 --- a/packages/skills/skills/inferencex-api/references/dashboard-views.md +++ b/packages/skills/skills/inferencex-api/references/dashboard-views.md @@ -172,29 +172,29 @@ All paths below are relative to `/api/v1/views/`. The maintained exhaustive query-key list is in the app's OpenAPI contract. The following groups explain which API selectors belong together. -| View | Selection and calculation | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `options` | Static registries and defaults, JSON only. Data-dependent run/config choices come from their own views. | -| `inference` | Model, sequence, precisions, date/exact run, GPU/vendor/framework/deployment/spec/power filters; metric, xmode, xstat, xmetric, percentile, optimal/best/allPoints; API-only equal-service sources/target; roleShare and powerFit panels; TCO, custom costs/powers, pricing, unofficial runs, comparison dates/endpoints. JSON or CSV. | -| `historical` | Model, sequence, target, metric, precisions, GPUs/vendors/frameworks/deployment, start/end, TCO and pricing. `extendToDate` labels synthetic extension, defaults to current UTC date. JSON or CSV. | -| `calculator` | Model/sequence/run/date, precisions/GPUs, percentile, target/mode, token type, owning/renting cost, TCO, power budget, cost cap, hide-above-limit and public unofficial runs. JSON or CSV. | -| `first-token` | Benchmark selection plus 1–8 positive TTFT caps in seconds, minimum interactivity, cost provider and token type. Shared dashboard winner selection. | -| `cache-reuse` | AgentX selection plus exact `config` from returned configurations and `recipe` from data.recipes. Cache-reuse curves retain official versus unofficial evidence. | -| `profit-estimator` | AgentX selection, comparison dates/runs, target, utilization, lab revenue share, list/OpenRouter/custom token prices, own/rent/custom chip costs, TCO, actual/theoretical cache-hit mode and provisioned/modeled/compare power. USD/chip-hour. | -| `profit-estimator-per-gigawatt` | Same selectors; USD/GW-year basis and owning-cost default. | -| `fleet` | Model/sequence/precision/GPUs, target/percentile, TCO/token type/cost provider, MW, input/output prices, ramp, cache discount, MTBI, recovery, horizon and metric. JSON or CSV. | -| `evaluation` | Model, task, date, precision and GPU selection; public unofficial runs remain separately labeled and independent of the official date cutoff. JSON or CSV. | -| `reliability` | Rolling range, GPU selection and optional `asOf` date for reproducibility. JSON or CSV. | -| `gpu-specs` | Full hardware properties or selected metric ranking; table/radar/bar are renderings of these values. JSON or CSV. | -| `overview` | Models, model/hardware row limits, hardware tier, engine, comparison mode and reference. JSON or CSV. | -| `rankings` | Ranking kind (model or chip), selected model/scenario and format. Use the exact values in OpenAPI. | -| `compare` | GPU pairs, model/slug, scenario, tier selection and variant. JSON or CSV. | -| `collectivex` | Ordered run selection and suite; EP size/phase/modes/precision/operation/percentile/axis/SKU/backend/series; KV page size/x/y/pull/push/overlap ISL/series; swap direction/layout/metric/percentile/series. | -| `submissions` | Search, table sort/direction/offset/limit, weekly/cumulative chart, on-change cutoff and NVIDIA/AMD/total lines. Table search does not filter the independent submission-volume chart. | -| `current-inferencex-image` | Model, sequence, hardware, precision, speculation, node type, framework families, `asOf` for image age/release status, and `includeHistory` for daily age history. | -| `gpu-metrics` | Required run, file/host artifact, GPU indices, metric or correlation axes, statistics sorting, chart mode and interactive downsampling preference. One telemetry source per run; full-record statistics computed on read. Not a serving-window/role-pool projection. Responses are no-store. | -| `ubenchx` | Test selector (`test`: `mem-bw`, `sm-l2-distance`, `tpc-skyline`, or `all`) plus GPU selector (`all` or one GPU). `mem-bw` returns static device-memory copy bandwidth rows with derived MBU and latency. `sm-l2-distance` returns SM-to-SM L2 latency difference matrix with GPC/die topology and summary statistics. `tpc-skyline` returns measured TPCs per GPC (sorted descending) with GPC, TPC, and SM counts, defaulting to every GPU. `all` (default) returns every test. The Y-axis selector on `/ubenchx/mem-bw` is presentation-only. | -| `video` | CI run/artifact discovery; only already-published artifact reads; source, serving cell, media/fidelity slot, power phase and GPU denominator; same-workload comparisons, axes, deployment costs and selected tradeoff point. No-store. | +| View | Selection and calculation | +| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `options` | Static registries and defaults, JSON only. Data-dependent run/config choices come from their own views. | +| `inference` | Model, sequence, precisions, date/exact run, GPU/vendor/framework/deployment/spec/power filters; metric, xmode, xstat, xmetric, percentile, optimal/best/allPoints; API-only equal-service sources/target; roleShare and powerFit panels; TCO, custom costs/powers, pricing, unofficial runs, comparison dates/endpoints. JSON or CSV. | +| `historical` | Model, sequence, target, metric, precisions, GPUs/vendors/frameworks/deployment, start/end, TCO and pricing. `extendToDate` labels synthetic extension, defaults to current UTC date. JSON or CSV. | +| `calculator` | Model/sequence/run/date, precisions/GPUs, percentile, target/mode, token type, owning/renting cost, TCO, power budget, cost cap, hide-above-limit and public unofficial runs. JSON or CSV. | +| `first-token` | Benchmark selection plus 1–8 positive TTFT caps in seconds, minimum interactivity, cost provider and token type. Shared dashboard winner selection. | +| `cache-reuse` | AgentX selection plus exact `config` from returned configurations and `recipe` from data.recipes. Cache-reuse curves retain official versus unofficial evidence. | +| `profit-estimator` | AgentX selection, comparison dates/runs, target, utilization, lab revenue share, list/OpenRouter/custom token prices, own/rent/custom chip costs, TCO, actual/theoretical cache-hit mode and provisioned/modeled/compare power. USD/chip-hour. | +| `profit-estimator-per-gigawatt` | Same selectors; USD/GW-year basis and owning-cost default. | +| `fleet` | Model/sequence/precision/GPUs, target/percentile, TCO/token type/cost provider, MW, input/output prices, ramp, cache discount, MTBI, recovery, horizon and metric. JSON or CSV. | +| `evaluation` | Model, task, date, precision and GPU selection; public unofficial runs remain separately labeled and independent of the official date cutoff. JSON or CSV. | +| `reliability` | Rolling range, GPU selection and optional `asOf` date for reproducibility. JSON or CSV. | +| `gpu-specs` | Full hardware properties or selected metric ranking; table/radar/bar are renderings of these values. JSON or CSV. | +| `overview` | Models, model/hardware row limits, hardware tier, engine, comparison mode and reference. JSON or CSV. | +| `rankings` | Ranking kind (model or chip), selected model/scenario and format. Use the exact values in OpenAPI. | +| `compare` | GPU pairs, model/slug, scenario, tier selection and variant. JSON or CSV. | +| `collectivex` | Ordered run selection and suite; EP size/phase/modes/precision/operation/percentile/axis/SKU/backend/series; KV page size/x/y/pull/push/overlap ISL/series; swap direction/layout/metric/percentile/series. | +| `submissions` | Search, table sort/direction/offset/limit, weekly/cumulative chart, on-change cutoff and NVIDIA/AMD/total lines. Table search does not filter the independent submission-volume chart. | +| `current-inferencex-image` | Model, sequence, hardware, precision, speculation, node type, framework families, `asOf` for image age/release status, and `includeHistory` for daily age history. | +| `gpu-metrics` | Required run, file/host artifact, GPU indices, metric or correlation axes, statistics sorting, chart mode and interactive downsampling preference. One telemetry source per run; full-record statistics computed on read. Not a serving-window/role-pool projection. Responses are no-store. | +| `ubenchx` | Test selector (`test`: `mem-bw`, `sm-l2-distance`, `tpc-skyline`, or `all`) plus GPU selector (`all` or one GPU). `mem-bw` returns static device-memory copy bandwidth rows with derived MBU and latency. The Huawei key is `Huawei 950PR` (measured SKU `Ascend950PR_9579`): FP16 rotating-pool graph timings, 360 observations per size, and explicit 1,600 GB/s reference assumptions in `peakBandwidthSource`. `metadata.protocol`, CANN/torch_npu versions, and absent sizes distinguish them from the GPU protocol. `timeMs` is amortized graph-copy time for these rows; optional row fields retain p10/p90 bandwidth, process medians, slots and working-set bytes. An explicit device filter leaves unavailable tests empty. `sm-l2-distance` returns SM-to-SM L2 latency difference matrix with GPC/die topology and summary statistics. `tpc-skyline` returns measured TPCs per GPC (sorted descending) with GPC, TPC, and SM counts, defaulting to every GPU. `all` (default) returns every test. The Y-axis selector on `/ubenchx/mem-bw` is presentation-only. | +| `video` | CI run/artifact discovery; only already-published artifact reads; source, serving cell, media/fidelity slot, power phase and GPU denominator; same-workload comparisons, axes, deployment costs and selected tradeoff point. No-store. | ## Interpretation and maintenance