Forked from bvaughn/react-virtualized-framerate-test.js
Last active
June 24, 2020 22:10
-
-
Save tryggvigy/586ae00524819cab158341fe24ed92a5 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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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(`${this._framerates.length} – ${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 Home */ | |
function testCase (completedCallback) { | |
const element = document.querySelector('.main-view-container .os-viewport') | |
element.scrollTop = 0 | |
const maxScrollTop = element.scrollHeight | |
let interval = 1 | |
let scrollTop = 0 | |
function incrementScrollTop () { | |
interval *= 1.1 | |
scrollTop = Math.min(scrollTop + interval, maxScrollTop) | |
element.scrollTop = scrollTop | |
if (scrollTop < maxScrollTop) { | |
requestAnimationFrame(incrementScrollTop) | |
} else { | |
completedCallback() | |
} | |
} | |
incrementScrollTop() | |
} | |
const testRunner = new TestRunner(testCase, 12) | |
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