Skip to content

Instantly share code, notes, and snippets.

@bvaughn
Last active November 7, 2024 09:43
Show Gist options
  • Save bvaughn/1fc166d5cb3f8c174a1552eeaeeaa0e6 to your computer and use it in GitHub Desktop.
Save bvaughn/1fc166d5cb3f8c174a1552eeaeeaa0e6 to your computer and use it in GitHub Desktop.
Quick demonstration of a way to measure scrolling performance for react-virtualized in an automated way
/** Measures framerate for the time between start() and stop() calls */
function FramerateMeasurer () {
this.start = () => {
this._beginTime = ( performance || Date ).now()
this._frames = 0
this._animationFrameId = requestAnimationFrame(this._loop)
}
this.stop = () => {
const endTime = ( performance || Date ).now()
if (this._animationFrameId) {
cancelAnimationFrame(this._animationFrameId)
}
return {
beginTime: this._beginTime,
endTime,
framerate: (this._frames * 1000) / (endTime - this._beginTime),
frames: this._frames
}
}
this._loop = () => {
this._frames++
this._animationFrameId = requestAnimationFrame(this._loop)
}
}
/** Runs an async test and measures its framerate until a confidence interface is achieved */
function TestRunner (testCase, minSampleSize = 5) {
const framerateMeasurer = new FramerateMeasurer()
this.start = () => {
this._framerates = []
this.isRunning = true
this._runTest()
}
this.stop = () => {
this.isRunning = false
console.log(`${this._framerates.length} framerate measurements taken, mean is: ${Statistics.calculateMean(this._framerates)}`)
}
this._getTestConfidence = () => {
if (this._framerates.length >= minSampleSize) {
const indices = this._framerates.map((framerate, index) => index)
const regressionSlope = Statistics.calculateRegressionSlope(
indices,
Statistics.calculateMean(indices),
this._framerates,
Statistics.calculateMean(this._framerates)
)
return regressionSlope >= 0
} else {
return false
}
}
this._runTest = () => {
framerateMeasurer.start()
testCase(this._onTestComplete)
}
this._onTestComplete = () => {
if (!this.isRunning) {
return
}
const measurements = framerateMeasurer.stop()
console.log(`${measurements.frames} frames, framerate: ${measurements.framerate}`)
this._framerates.push(measurements.framerate)
const isConfident = this._getTestConfidence(this._framerates, minSampleSize)
if (isConfident) {
this.stop()
} else {
this._runTest()
}
}
}
// Adapted from https://github.com/angular/angular/modules/benchpress/src/statistic.ts
const Statistics = {
calculateMean: function (samples) {
const total = samples.reduce((total, x) => {
total += x
return total
}, 0)
return total / samples.length
},
// See http://en.wikipedia.org/wiki/Simple_linear_regression
calculateRegressionSlope: function (xValues, xMean, yValues, yMean) {
let dividendSum = 0
let divisorSum = 0
for (var i = 0; i < xValues.length; i++) {
dividendSum += (xValues[i] - xMean) * (yValues[i] - yMean)
divisorSum += Math.pow(xValues[i] - xMean, 2)
}
return dividendSum / divisorSum
}
}
/** Tests a specific use case- scrolling a large FlexTable */
function testCase (completedCallback) {
const flexTable = document.querySelector('.FlexTable__Grid')
flexTable.scrollTop = 0
const maxScrollTop = flexTable.scrollHeight
let interval = 1
let scrollTop = 0
function incrementScrollTop () {
interval *= 1.1
scrollTop = Math.min(scrollTop + interval, maxScrollTop)
flexTable.scrollTop = scrollTop
if (scrollTop < maxScrollTop) {
requestAnimationFrame(incrementScrollTop)
} else {
completedCallback()
}
}
incrementScrollTop()
}
const testRunner = new TestRunner(testCase)
document.body.addEventListener('click',
function () {
if (testRunner.isRunning) {
testRunner.stop()
} else {
testRunner.start()
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment