Skip to content

Commit

Permalink
[feat] Apply lazy loading on metrics in Run Page (#2030)
Browse files Browse the repository at this point in the history
  • Loading branch information
roubkar authored Aug 5, 2022
1 parent c33af2a commit f9a8610
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 65 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 3.12.2

- Apply lazy loading on metrics in Run Page (roubkar)

## 3.12.1 Aug 2, 2022

- Loosen version requirements for grpcio (alberttorosyan)
Expand Down
2 changes: 2 additions & 0 deletions aim/web/ui/src/components/kit/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import ActionCard from './ActionCard';
import InputWrapper from './Input';
import Card from './Card';
import SelectDropdown from './SelectDropdown';
import Spinner from './Spinner';

export {
AutoSuggestions,
Expand All @@ -33,6 +34,7 @@ export {
Menu,
Card,
SelectDropdown,
Spinner,
};

export type { IButtonProps };
6 changes: 4 additions & 2 deletions aim/web/ui/src/pages/RunDetail/RunDetail.scss
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@

&__tabsContainer {
background: #ffffff;
box-shadow: 0 toRem(4px) toRem(6px) rgba(144, 175, 218, 0.2);
border-bottom: $border-grey-lighter;
box-shadow: 0 toRem(4px) toRem(6px) rgba(144, 175, 218, 0.2), 0 1px 0 0 $cuddle-20;
border-top: $border-grey-lighter;
position: relative;
z-index: 1;
Expand Down Expand Up @@ -241,6 +240,9 @@
border-radius: 0.5rem;
background: $white;
&__chartBox {
display: flex;
align-items: center;
justify-content: center;
height: 24.5rem;
padding: 0.625rem;
.LineChart__container {
Expand Down
150 changes: 137 additions & 13 deletions aim/web/ui/src/pages/RunDetail/RunDetailMetricsAndSystemTab.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { memo } from 'react';
import * as React from 'react';
import _ from 'lodash-es';

import IllustrationBlock from 'components/IllustrationBlock/IllustrationBlock';
Expand All @@ -8,10 +8,14 @@ import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary';
import { ANALYTICS_EVENT_KEYS } from 'config/analytics/analyticsKeysMap';

import * as analytics from 'services/analytics';
import runDetailAppModel from 'services/models/runs/runDetailAppModel';

import { isSystemMetric } from 'utils/isSystemMetric';
import contextToString from 'utils/contextToString';
import alphabeticalSortComparator from 'utils/alphabeticalSortComparator';

import { IRunBatch, IRunDetailMetricsAndSystemTabProps } from './types';
import RunMetricCard from './RunMetricCard';
import useRunMetricsBatch from './hooks/useRunMetricsBatch';
import { IRunBatch, IRunDetailMetricsAndSystemTabProps } from './types';

function RunDetailMetricsAndSystemTab({
runHash,
Expand All @@ -20,14 +24,110 @@ function RunDetailMetricsAndSystemTab({
isSystem,
isRunBatchLoading,
}: IRunDetailMetricsAndSystemTabProps): React.FunctionComponentElement<React.ReactNode> {
useRunMetricsBatch({ runBatch, runTraces, runHash });
const containerRef = React.useRef<HTMLDivElement>(null);
const observerRef = React.useRef<IntersectionObserver>();
const [observerIsReady, setObserverIsReady] = React.useState(false);
const [visibleMetrics, setVisibleMetrics] = React.useState<
IRunDetailMetricsAndSystemTabProps['runTraces']['metric']
>([]);

React.useEffect(() => {
if (!!containerRef.current) {
let options = {
root: containerRef.current.parentElement?.parentElement,
rootMargin: '0px',
threshold: 0,
};
observerRef.current = new IntersectionObserver(
(entries: IntersectionObserverEntry[]) => {
let metrics: { name: string; context: string }[] = [];
entries.forEach((entry: IntersectionObserverEntry) => {
if (entry.isIntersecting) {
let metricName = entry.target.getAttribute('data-name')!;
let metricContext = entry.target.getAttribute('data-context')!;
if (
!runBatch?.find(
(batch: IRunBatch) =>
batch.name === metricName &&
contextToString(batch.context) === metricContext,
)
) {
metrics.push({
name: metricName,
context: metricContext,
});
}
}
});

if (metrics.length > 0) {
setVisibleMetrics((vM) =>
vM.concat(
metrics.map(
(metric) =>
runTraces.metric.find(
(m) =>
m.name === metric.name &&
contextToString(m.context) === metric.context,
)!,
),
),
);
}
},
options,
);
setObserverIsReady(true);
}

analytics.pageView(
ANALYTICS_EVENT_KEYS.runDetails.tabs[isSystem ? 'system' : 'metrics']
.tabView,
);
}, [isSystem]);

return () => {
if (observerRef.current) {
observerRef.current.disconnect();
}
};
}, [isSystem, runBatch, observerIsReady, runTraces.metric]);

React.useEffect(() => {
let timerID: number;
let runsBatchRequestRef: { call: () => Promise<void>; abort: () => void };
if (visibleMetrics.length > 0) {
timerID = window.setTimeout(() => {
runsBatchRequestRef = runDetailAppModel.getRunMetricsBatch(
visibleMetrics,
runHash,
);

runsBatchRequestRef.call();
}, 100);
}

return () => {
if (timerID) {
clearTimeout(timerID);
}
if (runsBatchRequestRef) {
runsBatchRequestRef.abort();
}
};
}, [visibleMetrics, runHash]);

React.useEffect(() => {
setVisibleMetrics((vM) =>
vM.filter(
(m) =>
runBatch.findIndex(
(batch: IRunBatch) =>
batch.name === m.name &&
contextToString(batch.context) === contextToString(m.context),
) === -1,
),
);
}, [runBatch]);

return (
<ErrorBoundary>
Expand All @@ -36,14 +136,38 @@ function RunDetailMetricsAndSystemTab({
className='runDetailParamsTabLoader'
height='100%'
>
{!_.isEmpty(runBatch) ? (
<div className='RunDetailMetricsTab'>
{!_.isEmpty(runTraces.metric) ? (
<div className='RunDetailMetricsTab' ref={containerRef}>
<div className='RunDetailMetricsTab__container'>
{runBatch.map((batch: IRunBatch, i: number) => {
return (
<RunMetricCard key={batch.key} batch={batch} index={i} />
);
})}
{observerIsReady &&
runTraces.metric
.filter((m) =>
isSystem ? isSystemMetric(m.name) : !isSystemMetric(m.name),
)
.map((m) => ({
...m,
sortKey: `${m.name}_${contextToString(m.context)}`,
}))
.sort(alphabeticalSortComparator({ orderBy: 'sortKey' }))
.map((metric, i: number) => {
const batch: IRunBatch = {
...metric,
...runBatch?.find(
(batch: IRunBatch) =>
batch.name === metric.name &&
contextToString(batch.context) ===
contextToString(metric.context),
),
};
return (
<RunMetricCard
key={`${batch.name}_${contextToString(batch.context)}`}
batch={batch}
index={i}
observer={observerRef.current}
/>
);
})}
</div>
</div>
) : (
Expand All @@ -58,4 +182,4 @@ function RunDetailMetricsAndSystemTab({
);
}

export default memo(RunDetailMetricsAndSystemTab);
export default React.memo(RunDetailMetricsAndSystemTab);
72 changes: 45 additions & 27 deletions aim/web/ui/src/pages/RunDetail/RunMetricCard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { memo } from 'react';
import * as React from 'react';

import LineChart from 'components/LineChart/LineChart';
import { Badge, Text } from 'components/kit';
import { Badge, Text, Spinner } from 'components/kit';
import ErrorBoundary from 'components/ErrorBoundary/ErrorBoundary';
import { HighlightEnum } from 'components/HighlightModesPopover/HighlightModesPopover';

Expand All @@ -17,35 +17,53 @@ import { IRunMetricCardProps } from './types';
function RunMetricCard({
batch,
index,
observer,
}: IRunMetricCardProps): React.FunctionComponentElement<React.ReactNode> {
const containerRef = React.useRef(null);

React.useEffect(() => {
if (containerRef.current && observer) {
observer.observe(containerRef.current!);
}
}, [observer]);

return (
<ErrorBoundary>
<div className='RunDetailMetricsTab__container__chartContainer'>
<div
className='RunDetailMetricsTab__container__chartContainer'
data-name={batch.name}
data-context={contextToString(batch.context)}
ref={containerRef}
>
<div className='RunDetailMetricsTab__container__chartContainer__chartBox'>
<ErrorBoundary>
<LineChart
data={[
{
key: batch.key,
data: {
xValues: [...batch.iters],
yValues: [...batch.values],
{batch.iters ? (
<ErrorBoundary>
<LineChart
data={[
{
key: batch.key,
data: {
xValues: [...batch.iters],
yValues: [...batch.values],
},
color: '#1c2852',
dasharray: 'none',
selectors: [batch.key],
},
color: '#1c2852',
dasharray: 'none',
selectors: [batch.key],
},
]}
index={index}
axesScaleType={{
xAxis: ScaleEnum.Linear,
yAxis: ScaleEnum.Linear,
}}
ignoreOutliers={false}
highlightMode={HighlightEnum.Off}
curveInterpolation={CurveEnum.Linear}
/>
</ErrorBoundary>
]}
index={index}
axesScaleType={{
xAxis: ScaleEnum.Linear,
yAxis: ScaleEnum.Linear,
}}
ignoreOutliers={false}
highlightMode={HighlightEnum.Off}
curveInterpolation={CurveEnum.Linear}
/>
</ErrorBoundary>
) : (
<Spinner />
)}
</div>
<div className='RunDetailMetricsTab__container__chartContainer__metricDetailBox'>
<Text
Expand Down Expand Up @@ -76,4 +94,4 @@ function RunMetricCard({
);
}

export default memo(RunMetricCard);
export default React.memo(RunMetricCard);
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ function RunOverviewTab({ runData, runHash }: IRunOverviewTabProps) {
const [containerHeight, setContainerHeight] = React.useState<number>(0);

useRunMetricsBatch({
runBatch: runData.runMetricsBatch,
runTraces: runData.runTraces,
runHash,
});
Expand Down
9 changes: 4 additions & 5 deletions aim/web/ui/src/pages/RunDetail/hooks/useRunMetricsBatch.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import React from 'react';
import _ from 'lodash-es';

import runDetailAppModel from 'services/models/runs/runDetailAppModel';

function useRunMetricsBatch({ runBatch, runTraces, runHash }: any) {
function useRunMetricsBatch({ runTraces, runHash }: any) {
React.useEffect(() => {
const runsBatchRequestRef = runDetailAppModel.getRunMetricsBatch(
runTraces.metric,
runHash,
);
if (!runBatch && !_.isNil(runTraces)) {
runsBatchRequestRef.call();
}

runsBatchRequestRef.call();

return () => {
runsBatchRequestRef.abort();
};
Expand Down
10 changes: 9 additions & 1 deletion aim/web/ui/src/pages/RunDetail/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@ export interface IRunsProps {
}
export interface IRunDetailMetricsAndSystemTabProps {
runHash: string;
runTraces: any;
runTraces: {
metric: {
name: string;
context: {
[key: string]: unknown;
};
}[];
};
runBatch: any;
isSystem?: boolean;
isRunBatchLoading: boolean;
Expand Down Expand Up @@ -84,6 +91,7 @@ export interface ITraceVisualizerProps {
export interface IRunMetricCardProps {
batch: IRunBatch;
index: number;
observer: IntersectionObserver | undefined;
}

export interface IImagesVisualizerProps extends ITraceVisualizerProps {}
Expand Down
Loading

0 comments on commit f9a8610

Please sign in to comment.