Skip to content

Commit

Permalink
add dynamic updateFrequency via settingsRegistry (#182)
Browse files Browse the repository at this point in the history
* add dynamic updateFrequency via settingsRegistry

* update default to 100ms

* fix size constraint class issue
  • Loading branch information
AjayThorve authored Feb 27, 2024
1 parent b103605 commit 20789f0
Show file tree
Hide file tree
Showing 16 changed files with 447 additions and 95 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@jupyterlab/coreutils": "^6.0.0",
"@jupyterlab/launcher": "^4.0.5",
"@jupyterlab/services": "^7.0.0",
"@jupyterlab/settingregistry": "^4.1.0",
"d3-format": "^3.1.0",
"d3-scale": "^4.0.2",
"react": "^18.2.0",
Expand Down
15 changes: 15 additions & 0 deletions schema/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"title": "jupyterlab-nvdashboard",
"description": "Settings for the jupyterlab-nvdashboard extension.",
"type": "object",
"properties": {
"updateFrequency": {
"type": "integer",
"title": "Frequency of Updates",
"description": "The frequency of updates for the GPU Dashboard widgets, in milliseconds.",
"default": 100,
"minimum": 1
}
},
"additionalProperties": false
}
7 changes: 7 additions & 0 deletions src/assets/constants.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
export const BAR_COLOR_LINEAR_RANGE: string[] = ['#ff7900', '#b30000'];
export const GPU_COLOR_CATEGORICAL_RANGE: string[] = ['#fecc5c', '#bd0026'];
export const PLUGIN_ID = 'jupyterlab-nvdashboard';
export const PLUGIN_ID_CONFIG = `${PLUGIN_ID}:config`;
export const PLUGIN_ID_OPEN_SETTINGS = `${PLUGIN_ID}:open-settings`;
export const WIDGET_TRACKER_NAME = 'gpu-dashboard-widgets';
export const COMMAND_OPEN_SETTINGS = 'settingeditor:open';
export const COMMAND_OPEN_WIDGET = 'gpu-dashboard-widget:open';
export const DEFAULT_UPDATE_FREQUENCY = 100; // ms
35 changes: 35 additions & 0 deletions src/assets/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { SetStateAction, useEffect } from 'react';
import { DEFAULT_UPDATE_FREQUENCY, PLUGIN_ID_CONFIG } from './constants';

function loadSettingRegistry(
settingRegistry: ISettingRegistry,
setUpdateFrequency: {
(value: SetStateAction<number>): void;
(arg0: number): void;
}
) {
useEffect(() => {
const loadSettings = async () => {
try {
const settings = await settingRegistry.load(PLUGIN_ID_CONFIG);
const loadedUpdateFrequency =
(settings.get('updateFrequency').composite as number) ||
DEFAULT_UPDATE_FREQUENCY;
setUpdateFrequency(loadedUpdateFrequency);

settings.changed.connect(() => {
setUpdateFrequency(
(settings.get('updateFrequency').composite as number) ||
DEFAULT_UPDATE_FREQUENCY
);
});
} catch (error) {
console.error(`An error occurred while loading settings: ${error}`);
}
};
loadSettings();
}, []);
}

export default loadSettingRegistry;
20 changes: 20 additions & 0 deletions src/assets/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ILabShell, JupyterFrontEnd } from '@jupyterlab/application';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { MainAreaWidget, WidgetTracker } from '@jupyterlab/apputils';

export interface IChartProps {
settingRegistry: ISettingRegistry;
}

export interface IControlProps {
app: JupyterFrontEnd;
labShell: ILabShell;
tracker: WidgetTracker;
settingRegistry: ISettingRegistry;
}

export interface IWidgetInfo {
id: string;
title: string;
instance: MainAreaWidget;
}
26 changes: 22 additions & 4 deletions src/charts/GpuMemoryChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,26 @@ import {
} from 'recharts';
import { scaleLinear } from 'd3-scale';
import { renderCustomTooltip } from '../components/tooltipUtils';
import { BAR_COLOR_LINEAR_RANGE } from '../assets/constants';
import {
BAR_COLOR_LINEAR_RANGE,
DEFAULT_UPDATE_FREQUENCY
} from '../assets/constants';
import { format } from 'd3-format';
import AutoSizer from 'react-virtualized-auto-sizer';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import loadSettingRegistry from '../assets/hooks';
import { IChartProps } from '../assets/interfaces';

const GpuMemoryChart = (): JSX.Element => {
const GpuMemoryChart: React.FC<IChartProps> = ({
settingRegistry
}): JSX.Element => {
const [gpuMemory, setGpuMemory] = useState([]);
const [gpuTotalMemory, setGpuTotalMemory] = useState([]);
const [updateFrequency, setUpdateFrequency] = useState<number>(
DEFAULT_UPDATE_FREQUENCY
);

loadSettingRegistry(settingRegistry, setUpdateFrequency);

useEffect(() => {
async function fetchGPUMemory() {
Expand All @@ -39,7 +52,7 @@ const GpuMemoryChart = (): JSX.Element => {
}
const intervalId = setInterval(() => {
fetchGPUMemory();
}, 1000);
}, updateFrequency);

return () => clearInterval(intervalId);
}, []);
Expand Down Expand Up @@ -105,7 +118,12 @@ const GpuMemoryChart = (): JSX.Element => {
};

export class GpuMemoryChartWidget extends ReactWidget {
constructor(private settingRegistry: ISettingRegistry) {
super();
this.addClass('size-constrained-widgets');
this.settingRegistry = settingRegistry;
}
render(): JSX.Element {
return <GpuMemoryChart />;
return <GpuMemoryChart settingRegistry={this.settingRegistry} />;
}
}
32 changes: 22 additions & 10 deletions src/charts/GpuResourceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ import { requestAPI } from '../handler';
import { CustomLineChart } from '../components/customLineChart';
import { formatDate, formatBytes } from '../components/formatUtils';
import { scaleLinear } from 'd3-scale';
import { GPU_COLOR_CATEGORICAL_RANGE } from '../assets/constants';
import {
DEFAULT_UPDATE_FREQUENCY,
GPU_COLOR_CATEGORICAL_RANGE
} from '../assets/constants';
import { pauseIcon, playIcon } from '../assets/icons';
import loadSettingRegistry from '../assets/hooks';
import { IChartProps } from '../assets/interfaces';
import { ISettingRegistry } from '@jupyterlab/settingregistry';

interface IChartProps {
interface IDataProps {
time: number;
gpu_utilization_total: number;
gpu_memory_total: number;
Expand All @@ -19,15 +25,20 @@ interface IChartProps {
gpu_memory_individual: number[];
}

const GpuResourceChart = () => {
const [gpuData, setGpuData] = useState<IChartProps[]>([]);
const [tempData, setTempData] = useState<IChartProps[]>([]);
const GpuResourceChart: React.FC<IChartProps> = ({ settingRegistry }) => {
const [gpuData, setGpuData] = useState<IDataProps[]>([]);
const [tempData, setTempData] = useState<IDataProps[]>([]);
const [isPaused, setIsPaused] = useState(false);
const ngpus = gpuData[0]?.gpu_utilization_individual.length || 0;
const [updateFrequency, setUpdateFrequency] = useState<number>(
DEFAULT_UPDATE_FREQUENCY
);

loadSettingRegistry(settingRegistry, setUpdateFrequency);

useEffect(() => {
async function fetchGpuUsage() {
const response = await requestAPI<IChartProps>('gpu_resource');
const response = await requestAPI<IDataProps>('gpu_resource');
if (!isPaused) {
setGpuData(prevData => {
if (tempData.length > 1) {
Expand All @@ -42,7 +53,7 @@ const GpuResourceChart = () => {
}
}

const interval = setInterval(fetchGpuUsage, 1000);
const interval = setInterval(fetchGpuUsage, updateFrequency);

return () => clearInterval(interval);
}, [isPaused, tempData]);
Expand Down Expand Up @@ -216,12 +227,13 @@ const GpuResourceChart = () => {
};

export class GpuResourceChartWidget extends ReactWidget {
constructor() {
constructor(private settingRegistry: ISettingRegistry) {
super();
/* Time series charts need to have a min height for seekbar to be visible without scrolling*/
this.addClass('size-constrained-widgets-lg');
this.settingRegistry = settingRegistry;
}
render() {
return <GpuResourceChart />;
render(): JSX.Element {
return <GpuResourceChart settingRegistry={this.settingRegistry} />;
}
}
27 changes: 23 additions & 4 deletions src/charts/GpuUtilizationChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,24 @@ import {
} from 'recharts';
import { scaleLinear } from 'd3-scale';
import { renderCustomTooltip } from '../components/tooltipUtils';
import { BAR_COLOR_LINEAR_RANGE } from '../assets/constants';
import {
BAR_COLOR_LINEAR_RANGE,
DEFAULT_UPDATE_FREQUENCY
} from '../assets/constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { IChartProps } from '../assets/interfaces';
import loadSettingRegistry from '../assets/hooks';

const GpuUtilizationChart = (): JSX.Element => {
const GpuUtilizationChart: React.FC<IChartProps> = ({
settingRegistry
}): JSX.Element => {
const [gpuUtilization, setGpuUtilization] = useState([]);
const [updateFrequency, setUpdateFrequency] = useState<number>(
DEFAULT_UPDATE_FREQUENCY
);

loadSettingRegistry(settingRegistry, setUpdateFrequency);

useEffect(() => {
async function fetchGPUUtilization() {
Expand All @@ -34,7 +47,7 @@ const GpuUtilizationChart = (): JSX.Element => {
}
const intervalId = setInterval(() => {
fetchGPUUtilization();
}, 1000);
}, updateFrequency);

return () => clearInterval(intervalId);
}, []);
Expand Down Expand Up @@ -99,7 +112,13 @@ const GpuUtilizationChart = (): JSX.Element => {
};

export class GpuUtilizationChartWidget extends ReactWidget {
constructor(private settingRegistry: ISettingRegistry) {
super();
this.addClass('size-constrained-widgets');
this.settingRegistry = settingRegistry;
}

render(): JSX.Element {
return <GpuUtilizationChart />;
return <GpuUtilizationChart settingRegistry={this.settingRegistry} />;
}
}
32 changes: 22 additions & 10 deletions src/charts/MachineResourceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,16 @@ import { requestAPI } from '../handler';
import { CustomLineChart } from '../components/customLineChart';
import { formatDate, formatBytes } from '../components/formatUtils';
import { scaleLinear } from 'd3-scale';
import { GPU_COLOR_CATEGORICAL_RANGE } from '../assets/constants';
import {
DEFAULT_UPDATE_FREQUENCY,
GPU_COLOR_CATEGORICAL_RANGE
} from '../assets/constants';
import { pauseIcon, playIcon } from '../assets/icons';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { IChartProps } from '../assets/interfaces';
import loadSettingRegistry from '../assets/hooks';

interface IChartProps {
interface IDataProps {
time: number;
cpu_utilization: number;
memory_usage: number;
Expand All @@ -23,14 +29,19 @@ interface IChartProps {
network_write_current: number;
}

const MachineResourceChart = () => {
const [cpuData, setCpuData] = useState<IChartProps[]>([]);
const [tempData, setTempData] = useState<IChartProps[]>([]);
const MachineResourceChart: React.FC<IChartProps> = ({ settingRegistry }) => {
const [cpuData, setCpuData] = useState<IDataProps[]>([]);
const [tempData, setTempData] = useState<IDataProps[]>([]);
const [isPaused, setIsPaused] = useState(false);
const [updateFrequency, setUpdateFrequency] = useState<number>(
DEFAULT_UPDATE_FREQUENCY
);

loadSettingRegistry(settingRegistry, setUpdateFrequency);

useEffect(() => {
async function fetchCpuUsage() {
let response = await requestAPI<IChartProps>('cpu_resource');
let response = await requestAPI<IDataProps>('cpu_resource');

if (cpuData.length > 0) {
response = {
Expand Down Expand Up @@ -59,7 +70,7 @@ const MachineResourceChart = () => {
}
}

const interval = setInterval(fetchCpuUsage, 1000);
const interval = setInterval(fetchCpuUsage, updateFrequency);

return () => clearInterval(interval);
}, [isPaused, tempData]);
Expand Down Expand Up @@ -201,12 +212,13 @@ const MachineResourceChart = () => {
};

export class MachineResourceChartWidget extends ReactWidget {
constructor() {
constructor(private settingRegistry: ISettingRegistry) {
super();
/* Time series charts need to have a min height for seekbar to be visible without scrolling*/
this.addClass('size-constrained-widgets-lg');
this.settingRegistry = settingRegistry;
}
render() {
return <MachineResourceChart />;
render(): JSX.Element {
return <MachineResourceChart settingRegistry={this.settingRegistry} />;
}
}
32 changes: 24 additions & 8 deletions src/charts/NvLinkThroughputChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,32 @@ import { BarChart, Bar, Cell, YAxis, XAxis, Tooltip } from 'recharts';
import { scaleLinear } from 'd3-scale';
import { renderCustomTooltip } from '../components/tooltipUtils';
import { format } from 'd3-format';
import { BAR_COLOR_LINEAR_RANGE } from '../assets/constants';
import {
BAR_COLOR_LINEAR_RANGE,
DEFAULT_UPDATE_FREQUENCY
} from '../assets/constants';
import AutoSizer from 'react-virtualized-auto-sizer';
import { IChartProps } from '../assets/interfaces';
import loadSettingRegistry from '../assets/hooks';
import { ISettingRegistry } from '@jupyterlab/settingregistry';

interface INvLinkChartProps {
interface IDataProps {
nvlink_tx: number[];
nvlink_rx: number[];
max_rxtx_bw: number;
}

const NvLinkThroughputChart = (): JSX.Element => {
const [nvlinkStats, setNvLinkStats] = useState<INvLinkChartProps>();
const NvLinkThroughputChart: React.FC<IChartProps> = ({ settingRegistry }) => {
const [nvlinkStats, setNvLinkStats] = useState<IDataProps>();
const [updateFrequency, setUpdateFrequency] = useState<number>(
DEFAULT_UPDATE_FREQUENCY
);

loadSettingRegistry(settingRegistry, setUpdateFrequency);

useEffect(() => {
async function fetchGPUMemory() {
const response = await requestAPI<INvLinkChartProps>('nvlink_throughput');
const response = await requestAPI<IDataProps>('nvlink_throughput');
console.log(response);
setNvLinkStats(response);
}
Expand All @@ -29,12 +40,12 @@ const NvLinkThroughputChart = (): JSX.Element => {

useEffect(() => {
async function fetchGPUMemory() {
const response = await requestAPI<INvLinkChartProps>('nvlink_throughput');
const response = await requestAPI<IDataProps>('nvlink_throughput');
setNvLinkStats(response);
}
const intervalId = setInterval(() => {
fetchGPUMemory();
}, 1000);
}, updateFrequency);

return () => clearInterval(intervalId);
}, []);
Expand Down Expand Up @@ -138,7 +149,12 @@ const NvLinkThroughputChart = (): JSX.Element => {
};

export class NvLinkThroughputChartWidget extends ReactWidget {
constructor(private settingRegistry: ISettingRegistry) {
super();
this.addClass('size-constrained-widgets');
this.settingRegistry = settingRegistry;
}
render(): JSX.Element {
return <NvLinkThroughputChart />;
return <NvLinkThroughputChart settingRegistry={this.settingRegistry} />;
}
}
Loading

0 comments on commit 20789f0

Please sign in to comment.