mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-05-05 04:57:42 +03:00
Support showing NPU stats in dashboard
This commit is contained in:
parent
f15f09d542
commit
b9c7b1caa6
@ -72,7 +72,9 @@
|
|||||||
"toast": {
|
"toast": {
|
||||||
"success": "Copied GPU info to clipboard"
|
"success": "Copied GPU info to clipboard"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"npuUsage": "NPU Usage",
|
||||||
|
"npuMemory": "NPU Memory"
|
||||||
},
|
},
|
||||||
"otherProcesses": {
|
"otherProcesses": {
|
||||||
"title": "Other Processes",
|
"title": "Other Processes",
|
||||||
|
|||||||
@ -4,6 +4,7 @@ export interface FrigateStats {
|
|||||||
detectors: { [detectorKey: string]: DetectorStats };
|
detectors: { [detectorKey: string]: DetectorStats };
|
||||||
embeddings?: EmbeddingsStats;
|
embeddings?: EmbeddingsStats;
|
||||||
gpu_usages?: { [gpuKey: string]: GpuStats };
|
gpu_usages?: { [gpuKey: string]: GpuStats };
|
||||||
|
npu_usages?: { [npuKey: string]: NpuStats };
|
||||||
processes: { [processKey: string]: ExtraProcessStats };
|
processes: { [processKey: string]: ExtraProcessStats };
|
||||||
service: ServiceStats;
|
service: ServiceStats;
|
||||||
detection_fps: number;
|
detection_fps: number;
|
||||||
@ -54,6 +55,11 @@ export type GpuStats = {
|
|||||||
pstate?: string;
|
pstate?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type NpuStats = {
|
||||||
|
npu: number;
|
||||||
|
mem: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type GpuInfo = "vainfo" | "nvinfo";
|
export type GpuInfo = "vainfo" | "nvinfo";
|
||||||
|
|
||||||
export type ServiceStats = {
|
export type ServiceStats = {
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export default function GeneralMetrics({
|
|||||||
const { data: initialStats } = useSWR<FrigateStats[]>(
|
const { data: initialStats } = useSWR<FrigateStats[]>(
|
||||||
[
|
[
|
||||||
"stats/history",
|
"stats/history",
|
||||||
{ keys: "cpu_usages,detectors,gpu_usages,processes,service" },
|
{ keys: "cpu_usages,detectors,gpu_usages,npu_usages,processes,service" },
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
revalidateOnFocus: false,
|
revalidateOnFocus: false,
|
||||||
@ -369,8 +369,57 @@ export default function GeneralMetrics({
|
|||||||
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
|
return Object.keys(series).length > 0 ? Object.values(series) : undefined;
|
||||||
}, [statsHistory]);
|
}, [statsHistory]);
|
||||||
|
|
||||||
|
// npu stats
|
||||||
|
|
||||||
|
const npuSeries = useMemo(() => {
|
||||||
|
if (!statsHistory) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const series: {
|
||||||
|
[key: string]: { name: string; data: { x: number; y: number }[] };
|
||||||
|
} = {};
|
||||||
|
let hasValidNpu = false;
|
||||||
|
|
||||||
|
statsHistory.forEach((stats, statsIdx) => {
|
||||||
|
if (!stats) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.entries(stats.npu_usages || []).forEach(([key, stats]) => {
|
||||||
|
if (!(key in series)) {
|
||||||
|
series[key] = { name: key, data: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.npu) {
|
||||||
|
hasValidNpu = true;
|
||||||
|
series[key].data.push({ x: statsIdx + 1, y: stats.npu });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasValidNpu) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(series).length > 0 ? Object.values(series) : [];
|
||||||
|
}, [statsHistory]);
|
||||||
|
|
||||||
// other processes stats
|
// other processes stats
|
||||||
|
|
||||||
|
const hardwareType = useMemo(() => {
|
||||||
|
const hasGpu = statsHistory[0]?.gpu_usages != undefined;
|
||||||
|
const hasNpu = statsHistory[0]?.npu_usages != undefined;
|
||||||
|
|
||||||
|
if (hasGpu && !hasNpu) {
|
||||||
|
return "GPUs";
|
||||||
|
} else if (!hasGpu && hasNpu) {
|
||||||
|
return "NPUs";
|
||||||
|
} else {
|
||||||
|
return "GPUs / NPUs";
|
||||||
|
}
|
||||||
|
}, [statsHistory]);
|
||||||
|
|
||||||
const otherProcessCpuSeries = useMemo(() => {
|
const otherProcessCpuSeries = useMemo(() => {
|
||||||
if (!statsHistory) {
|
if (!statsHistory) {
|
||||||
return [];
|
return [];
|
||||||
@ -533,11 +582,13 @@ export default function GeneralMetrics({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{(statsHistory.length == 0 || statsHistory[0].gpu_usages) && (
|
{(statsHistory.length == 0 ||
|
||||||
|
statsHistory[0].gpu_usages ||
|
||||||
|
statsHistory[0].npu_usages) && (
|
||||||
<>
|
<>
|
||||||
<div className="mt-4 flex items-center justify-between">
|
<div className="mt-4 flex items-center justify-between">
|
||||||
<div className="text-sm font-medium text-muted-foreground">
|
<div className="text-sm font-medium text-muted-foreground">
|
||||||
GPUs
|
{hardwareType}
|
||||||
</div>
|
</div>
|
||||||
{canGetGpuInfo && (
|
{canGetGpuInfo && (
|
||||||
<Button
|
<Button
|
||||||
@ -556,97 +607,127 @@ export default function GeneralMetrics({
|
|||||||
gpuEncSeries?.length && "md:grid-cols-4",
|
gpuEncSeries?.length && "md:grid-cols-4",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{statsHistory.length != 0 ? (
|
{statsHistory[0]?.gpu_usages && (
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
<>
|
||||||
<div className="mb-5">
|
{statsHistory.length != 0 ? (
|
||||||
{t("general.hardwareInfo.gpuUsage")}
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
</div>
|
<div className="mb-5">
|
||||||
{gpuSeries.map((series) => (
|
{t("general.hardwareInfo.gpuUsage")}
|
||||||
<ThresholdBarGraph
|
</div>
|
||||||
key={series.name}
|
{gpuSeries.map((series) => (
|
||||||
graphId={`${series.name}-gpu`}
|
<ThresholdBarGraph
|
||||||
name={series.name}
|
key={series.name}
|
||||||
unit="%"
|
graphId={`${series.name}-gpu`}
|
||||||
threshold={GPUUsageThreshold}
|
name={series.name}
|
||||||
updateTimes={updateTimes}
|
unit="%"
|
||||||
data={[series]}
|
threshold={GPUUsageThreshold}
|
||||||
/>
|
updateTimes={updateTimes}
|
||||||
))}
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<>
|
||||||
|
{gpuMemSeries && (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.gpuMemory")}
|
||||||
|
</div>
|
||||||
|
{gpuMemSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-mem`}
|
||||||
|
unit="%"
|
||||||
|
name={series.name}
|
||||||
|
threshold={GPUMemThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<>
|
||||||
|
{gpuEncSeries && gpuEncSeries?.length != 0 && (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.gpuEncoder")}
|
||||||
|
</div>
|
||||||
|
{gpuEncSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-enc`}
|
||||||
|
unit="%"
|
||||||
|
name={series.name}
|
||||||
|
threshold={GPUMemThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<>
|
||||||
|
{gpuDecSeries && gpuDecSeries?.length != 0 && (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.gpuDecoder")}
|
||||||
|
</div>
|
||||||
|
{gpuDecSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-dec`}
|
||||||
|
unit="%"
|
||||||
|
name={series.name}
|
||||||
|
threshold={GPUMemThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{statsHistory[0]?.npu_usages && (
|
||||||
|
<div
|
||||||
|
className={cn("mt-4 grid grid-cols-1 gap-2 sm:grid-cols-2")}
|
||||||
|
>
|
||||||
|
{statsHistory.length != 0 ? (
|
||||||
|
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
||||||
|
<div className="mb-5">
|
||||||
|
{t("general.hardwareInfo.npuUsage")}
|
||||||
|
</div>
|
||||||
|
{npuSeries.map((series) => (
|
||||||
|
<ThresholdBarGraph
|
||||||
|
key={series.name}
|
||||||
|
graphId={`${series.name}-npu`}
|
||||||
|
name={series.name}
|
||||||
|
unit="%"
|
||||||
|
threshold={GPUUsageThreshold}
|
||||||
|
updateTimes={updateTimes}
|
||||||
|
data={[series]}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<Skeleton className="aspect-video w-full" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
|
||||||
{statsHistory.length != 0 ? (
|
|
||||||
<>
|
|
||||||
{gpuMemSeries && (
|
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
|
||||||
<div className="mb-5">
|
|
||||||
{t("general.hardwareInfo.gpuMemory")}
|
|
||||||
</div>
|
|
||||||
{gpuMemSeries.map((series) => (
|
|
||||||
<ThresholdBarGraph
|
|
||||||
key={series.name}
|
|
||||||
graphId={`${series.name}-mem`}
|
|
||||||
unit="%"
|
|
||||||
name={series.name}
|
|
||||||
threshold={GPUMemThreshold}
|
|
||||||
updateTimes={updateTimes}
|
|
||||||
data={[series]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
|
||||||
{statsHistory.length != 0 ? (
|
|
||||||
<>
|
|
||||||
{gpuEncSeries && gpuEncSeries?.length != 0 && (
|
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
|
||||||
<div className="mb-5">
|
|
||||||
{t("general.hardwareInfo.gpuEncoder")}
|
|
||||||
</div>
|
|
||||||
{gpuEncSeries.map((series) => (
|
|
||||||
<ThresholdBarGraph
|
|
||||||
key={series.name}
|
|
||||||
graphId={`${series.name}-enc`}
|
|
||||||
unit="%"
|
|
||||||
name={series.name}
|
|
||||||
threshold={GPUMemThreshold}
|
|
||||||
updateTimes={updateTimes}
|
|
||||||
data={[series]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
|
||||||
{statsHistory.length != 0 ? (
|
|
||||||
<>
|
|
||||||
{gpuDecSeries && gpuDecSeries?.length != 0 && (
|
|
||||||
<div className="rounded-lg bg-background_alt p-2.5 md:rounded-2xl">
|
|
||||||
<div className="mb-5">
|
|
||||||
{t("general.hardwareInfo.gpuDecoder")}
|
|
||||||
</div>
|
|
||||||
{gpuDecSeries.map((series) => (
|
|
||||||
<ThresholdBarGraph
|
|
||||||
key={series.name}
|
|
||||||
graphId={`${series.name}-dec`}
|
|
||||||
unit="%"
|
|
||||||
name={series.name}
|
|
||||||
threshold={GPUMemThreshold}
|
|
||||||
updateTimes={updateTimes}
|
|
||||||
data={[series]}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="aspect-video w-full" />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user