From 8ed1bf0edac3bf9d18d515676aabdca371211ea0 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Sun, 8 Nov 2026 22:30:50 +0800 Subject: [PATCH] feat(gpu): show aggregate memory usage in monitor summary (#14349) --- frontend/src/views/ai/gpu/current/index.vue | 46 ++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/ai/gpu/current/index.vue b/frontend/src/views/ai/gpu/current/index.vue index 7e80fefb1..ffc56f291 100644 --- a/frontend/src/views/ai/gpu/current/index.vue +++ b/frontend/src/views/ai/gpu/current/index.vue @@ -75,7 +75,14 @@ :disabled="!summary.sharedMemoryOnly && !summary.memoryPartial" :content="summaryMemoryTooltip(summary)" > - + + {{ $t('home.used') }} {{ formatSummaryMemory(summary.memoryUsage.used) }} / + {{ formatSummaryMemory(summary.memoryUsage.total) }} + + + {{ formatSummaryMemory( summary.sharedMemoryOnly ? summary.sharedMemoryTotal : summary.memoryTotal, @@ -732,6 +739,41 @@ const formatSummaryMemory = (value: number | null) => { return value === null || !Number.isFinite(value) ? '—' : computeSizeForDocker(value); }; +const summaryMemoryUsage = (vendor?: string) => { + const devices = [ + ...[...gpuInfo.value.gpu, ...gpuInfo.value.npu] + .filter( + (device) => (!vendor || device.type === vendor) && (device.type === 'ascend' || !device.sharedMemory), + ) + .map((device) => ({ id: deviceKey(device), used: device.memUsed, total: device.memTotal })), + ...(!vendor || vendor === 'intel' || vendor === 'xpu' ? xpuInfo.value.xpu : []).map((device) => ({ + id: `xpu-${device.basic.deviceID}`, + used: device.stats.memoryUsed, + total: device.basic.memory, + })), + ]; + if (!devices.length) return null; + let used: number | null = 0; + let total: number | null = 0; + const seen = new Set(); + for (const device of devices) { + if (seen.has(device.id)) continue; + seen.add(device.id); + const deviceUsed = memoryMiB(device.used || ''); + const deviceTotal = memoryMiB(device.total || ''); + used = used !== null && deviceUsed !== null && deviceUsed >= 0 ? used + deviceUsed * 1024 ** 2 : null; + total = total !== null && deviceTotal !== null && deviceTotal >= 0 ? total + deviceTotal * 1024 ** 2 : null; + } + return { + used, + total, + percentage: + used !== null && total !== null && total > 0 + ? Math.min(100, Number(((used / total) * 100).toFixed(1))) + : null, + }; +}; + const overviewRows = computed(() => { if (gpuInfo.value.summaries?.length) { return gpuInfo.value.summaries @@ -748,6 +790,7 @@ const overviewRows = computed(() => { devices.length && !devices.some((device) => device.sharedMemory) ? null : summary.sharedMemoryTotal; return { ...summary, + memoryUsage: summaryMemoryUsage(summary.vendor), sharedMemoryTotal, cardCountText: summary.cardCount === null ? '—' : String(summary.cardCount), sharedMemoryOnly: @@ -796,6 +839,7 @@ const overviewRows = computed(() => { cudaVersion: gpuInfo.value.cudaVersion, cardCountText: deviceCountText.value, memoryTotal, + memoryUsage: summaryMemoryUsage(), sharedMemoryTotal, sharedMemoryOnly: sharedMemoryTotal !== null && memoryDevices.every((device) => device.shared), memoryPartial,