Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

feat(Provider): Rendering performance telemetry #2079

Merged
merged 7 commits into from
Nov 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Add bounce animation to button clicks in Teams theme @notandrew ([#1724](https://github.com/stardust-ui/react/pull/1724))
- Update Silver color scheme, adding `foregroundHover`, `foregroundPressed` and `background` definitions @pompomon ([#2078](https://github.com/microsoft/fluent-ui-react/pull/2078))

### Performance
- Add rendering performance telemetry @miroslavstastny ([#2079](https://github.com/microsoft/fluent-ui-react/pull/2079))
miroslavstastny marked this conversation as resolved.
Show resolved Hide resolved

<!--------------------------------[ v0.40.2 ]------------------------------- -->
## [v0.40.2](https://github.com/stardust-ui/react/tree/v0.40.2) (2019-10-30)
[Compare changes](https://github.com/stardust-ui/react/compare/v0.40.1...v0.40.2)
Expand Down
36 changes: 34 additions & 2 deletions docs/src/prototypes/customToolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
useSelectKnob,
KnobInspector,
} from '@stardust-ui/docs-components'
import { Provider, Flex, themes, mergeThemes } from '@stardust-ui/react'
import { Provider, Flex, themes, mergeThemes, Telemetry } from '@stardust-ui/react'

import { darkThemeOverrides } from './darkThemeOverrides'
import { highContrastThemeOverrides } from './highContrastThemeOverrides'
Expand Down Expand Up @@ -58,21 +58,53 @@ const CustomToolbarPrototype: React.FunctionComponent = () => {
const [currentSlide, setCurrentSlide] = React.useState(23)
const totalSlides = 34

const [telemetryEnabled] = useBooleanKnob({ name: 'telemetryEnabled', initialValue: true })
const telemetryRef = React.useRef<Telemetry>()

let theme = {}
if (themeName === 'teamsDark') {
theme = mergeThemes(themes.teamsDark, darkThemeOverrides)
} else if (themeName === 'teamsHighContrast') {
theme = mergeThemes(themes.teamsHighContrast, darkThemeOverrides, highContrastThemeOverrides)
}

React.useEffect(() => {
miroslavstastny marked this conversation as resolved.
Show resolved Hide resolved
performance.measure('render-custom-toolbar', 'render-custom-toolbar')
const telemetry = telemetryRef.current
if (!telemetryEnabled || !telemetry) {
return
}

telemetry.enabled = false

const totals = _.reduce(
telemetry.performance,
(acc, next) => {
acc.count += next.count
acc.msTotal += next.msTotal
return acc
},
{ count: 0, msTotal: 0 },
)

console.log(`Rendered ${totals.count} Stardust components in ${totals.msTotal} ms`)
console.table(telemetry.performance)
})

if (telemetryRef.current) {
telemetryRef.current.enabled = telemetryEnabled
telemetryRef.current.reset()
}
performance.mark('render-custom-toolbar')

return (
<div style={{ height: '100vh' }}>
<Flex column fill>
<KnobsSnippet>
<KnobInspector />
</KnobsSnippet>

<Provider theme={theme} rtl={rtl}>
<Provider theme={theme} rtl={rtl} {...(telemetryEnabled ? { telemetryRef } : undefined)}>
<Flex
hAlign="center"
styles={{
Expand Down
19 changes: 19 additions & 0 deletions packages/react/src/components/Provider/Provider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { IStyle } from 'fela'
import * as _ from 'lodash'
import * as customPropTypes from '@stardust-ui/react-proptypes'
import * as PropTypes from 'prop-types'
import * as React from 'react'
// @ts-ignore
Expand Down Expand Up @@ -28,6 +29,7 @@ import {
withSafeTypeForAs,
} from '../../types'
import mergeContexts from '../../lib/mergeProviderContexts'
import Telemetry from '../../lib/Telemetry'

export interface ProviderProps extends ChildrenComponentProps {
renderer?: Renderer
Expand All @@ -37,6 +39,7 @@ export interface ProviderProps extends ChildrenComponentProps {
target?: Document
theme?: ThemeInput
variables?: ComponentVariablesInput
telemetryRef?: React.Ref<Telemetry>
}

/**
Expand Down Expand Up @@ -76,6 +79,7 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {
disableAnimations: PropTypes.bool,
children: PropTypes.node.isRequired,
target: PropTypes.object,
telemetryRef: customPropTypes.ref,
}

static defaultProps = {
Expand All @@ -89,6 +93,8 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {
outgoingContext: ProviderContextPrepared
staticStylesRendered: boolean = false

telemetry: Telemetry

renderStaticStyles = (renderer: Renderer, mergedTheme: ThemePrepared) => {
const { siteVariables } = mergedTheme
const { staticStyles } = this.props.theme
Expand Down Expand Up @@ -153,14 +159,27 @@ class Provider extends React.Component<WithAsProp<ProviderProps>> {
target,
theme,
variables,
telemetryRef,
...unhandledProps
} = this.props

if (telemetryRef) {
if (!this.telemetry) {
this.telemetry = new Telemetry()
}

telemetryRef['current'] = this.telemetry
} else if (this.telemetry) {
delete this.telemetry
}

const inputContext: ProviderContextInput = {
theme,
rtl,
disableAnimations,
renderer,
target,
telemetry: this.telemetry,
}

const incomingContext: ProviderContextPrepared = overwrite ? {} : this.context
Expand Down
20 changes: 20 additions & 0 deletions packages/react/src/lib/Telemetry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type ComponentPerfStats = {
count: number
msTotal: number
msMin: number
msMax: number
}

export default class Telemetry {
performance: Record<string, ComponentPerfStats>
enabled: boolean

constructor() {
this.performance = {}
this.enabled = true
}

reset() {
this.performance = {}
}
}
1 change: 1 addition & 0 deletions packages/react/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@ export * from './commonPropInterfaces'
export { commonPropTypes }

export { default as withDebugId } from './withDebugId'
export { default as Telemetry } from './Telemetry'
8 changes: 2 additions & 6 deletions packages/react/src/lib/mergeProviderContexts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ const mergeProviderContexts = (
},
rtl: false,
disableAnimations: false,
originalThemes: [],
target: document, // eslint-disable-line no-undef
telemetry: undefined,
_internal_resolvedComponentVariables: {},
renderer: undefined,
}
Expand Down Expand Up @@ -84,11 +84,7 @@ const mergeProviderContexts = (
acc.disableAnimations = mergedDisableAnimations
}

const contextOriginalThemes = (next as ProviderContextPrepared).originalThemes
miroslavstastny marked this conversation as resolved.
Show resolved Hide resolved
? (next as ProviderContextPrepared).originalThemes
: [next.theme]

acc.originalThemes = [...acc.originalThemes, ...contextOriginalThemes]
acc.telemetry = next.telemetry || acc.telemetry

return acc
},
Expand Down
35 changes: 33 additions & 2 deletions packages/react/src/lib/renderComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import createAnimationStyles from './createAnimationStyles'
import { isEnabled as isDebugEnabled } from './debug/debugEnabled'
import { DebugData } from './debug/debugData'
import withDebugId from './withDebugId'
import Telemetry from './Telemetry'

export interface RenderResultConfig<P> {
ElementType: React.ElementType<P>
Expand Down Expand Up @@ -175,9 +176,12 @@ const renderComponent = <P extends {}>(
renderer = null,
rtl = false,
theme = emptyTheme,
telemetry = undefined as Telemetry,
_internal_resolvedComponentVariables: resolvedComponentVariables = {},
} = context || {}

const startTime = telemetry && telemetry.enabled ? performance.now() : 0

const ElementType = getElementType(props) as React.ReactType<P>
const stateAndProps = { ...state, ...props }

Expand Down Expand Up @@ -296,11 +300,38 @@ const renderComponent = <P extends {}>(
})
}

let result
if (accessibility.focusZone) {
return renderWithFocusZone(render, accessibility.focusZone, resolvedConfig)
result = renderWithFocusZone(render, accessibility.focusZone, resolvedConfig)
} else {
result = render(resolvedConfig)
}

if (telemetry && telemetry.enabled) {
const duration = performance.now() - startTime

if (telemetry.performance[displayName]) {
telemetry.performance[displayName].count++
telemetry.performance[displayName].msTotal += duration
telemetry.performance[displayName].msMin = Math.min(
duration,
telemetry.performance[displayName].msMin,
)
telemetry.performance[displayName].msMax = Math.max(
duration,
telemetry.performance[displayName].msMax,
)
} else {
telemetry.performance[displayName] = {
count: 1,
msTotal: duration,
msMin: duration,
msMax: duration,
}
}
}

return render(resolvedConfig)
return result
}

export default renderComponent
4 changes: 3 additions & 1 deletion packages/react/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import * as React from 'react'
import { ThemeInput, Renderer, ThemePrepared } from './themes/types'
import Telemetry from './lib/Telemetry'

export type Extendable<T, V = any> = T & {
[key: string]: V
Expand Down Expand Up @@ -159,6 +160,7 @@ export interface ProviderContextInput {
disableAnimations?: boolean
target?: Document
theme?: ThemeInput
telemetry?: Telemetry
}

export interface ProviderContextPrepared {
Expand All @@ -167,6 +169,6 @@ export interface ProviderContextPrepared {
disableAnimations: boolean
target: Document
theme: ThemePrepared
originalThemes: (ThemeInput | undefined)[]
telemetry: Telemetry | undefined
_internal_resolvedComponentVariables: Record<string, object>
}