Skip to content

Commit d4f6f86

Browse files
committed
Record computation time
Record computation time along with re-computation count
1 parent b2a85ee commit d4f6f86

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

src/index.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ export function setInputStabilityCheckEnabled(enabled: StabilityCheck) {
4646
globalStabilityCheck = enabled
4747
}
4848

49+
function getTime() {
50+
if (performance && typeof performance.now === 'function') {
51+
return performance.now()
52+
}
53+
54+
return Date.now()
55+
}
56+
4957
function getDependencies(funcs: unknown[]) {
5058
const dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs
5159

@@ -79,6 +87,7 @@ export function createSelectorCreator<
7987
) {
8088
const createSelector = (...funcs: Function[]) => {
8189
let recomputations = 0
90+
let computationTime = 0
8291
let lastResult: unknown
8392

8493
// Due to the intricacies of rest params, we can't do an optional arg after `...funcs`.
@@ -121,8 +130,12 @@ export function createSelectorCreator<
121130
const memoizedResultFunc = memoize(
122131
function recomputationWrapper() {
123132
recomputations++
133+
const computationStartTime = getTime()
124134
// apply arguments instead of spreading for performance.
125-
return resultFunc!.apply(null, arguments)
135+
const resultFuncOutput = resultFunc!.apply(null, arguments)
136+
computationTime += getTime() - computationStartTime
137+
138+
return resultFuncOutput
126139
} as F,
127140
...finalMemoizeOptions
128141
)
@@ -204,7 +217,8 @@ export function createSelectorCreator<
204217
dependencies,
205218
lastResult: () => lastResult,
206219
recomputations: () => recomputations,
207-
resetRecomputations: () => (recomputations = 0)
220+
resetRecomputations: () => (recomputations = 0, computationTime = 0),
221+
computationTime: () => computationTime
208222
})
209223

210224
return selector

src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ export interface OutputSelectorFields<Combiner extends UnknownFunction, Keys> {
3737
dependencies: SelectorArray
3838
/** Counts the number of times the output has been recalculated */
3939
recomputations: () => number
40-
/** Resets the count of recomputations count to 0 */
40+
/** Total computation time of everytime the output has been recalculated */
41+
computationTime: () => number
42+
/** Resets the count of recomputations count and computation time to 0 */
4143
resetRecomputations: () => number
4244
}
4345

test/reselect.spec.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
weakMapMemoize
1010
} from 'reselect'
1111
import lodashMemoize from 'lodash/memoize'
12-
import { vi } from 'vitest'
12+
import { afterAll, afterEach, beforeEach, describe, vi } from 'vitest'
1313
import { configureStore, createSlice, PayloadAction } from '@reduxjs/toolkit'
1414

1515
// Construct 1E6 states for perf test outside of the perf test so as to not change the execute time of the test function
@@ -101,6 +101,49 @@ describe('Basic selector behavior', () => {
101101
)
102102
})
103103

104+
describe('computation time', () => {
105+
const performanceSpy = vi.spyOn(performance, 'now')
106+
107+
beforeEach(() => {
108+
performanceSpy
109+
.mockReturnValueOnce(1500)
110+
.mockReturnValueOnce(3000)
111+
.mockReturnValueOnce(4500)
112+
.mockReturnValueOnce(6000)
113+
})
114+
115+
it('should records computation time for every re-computation', () => {
116+
const selector = createSelector(
117+
(state: StateAB) => state.a,
118+
(state: StateAB) => state.b,
119+
(a, b) => a + b
120+
)
121+
const state1 = { a: 1, b: 2 }
122+
selector(state1)
123+
expect(selector.computationTime()).toBe(1500)
124+
const state2 = { a: 3, b: 2 }
125+
selector(state2)
126+
expect(selector.computationTime()).toBe(3000)
127+
})
128+
129+
it('should reset computation time when calling resetRecomputations', () => {
130+
const selector = createSelector(
131+
(state: StateAB) => state.a,
132+
(state: StateAB) => state.b,
133+
(a, b) => a + b
134+
)
135+
const state1 = { a: 1, b: 2 }
136+
selector(state1)
137+
expect(selector.computationTime()).toBe(1500)
138+
selector.resetRecomputations()
139+
expect(selector.computationTime()).toBe(0)
140+
})
141+
142+
afterEach(() => {
143+
performanceSpy.mockClear()
144+
})
145+
})
146+
104147
describe('performance checks', () => {
105148
const originalEnv = process.env.NODE_ENV
106149

0 commit comments

Comments
 (0)