perf: add more benchmarks (#389)
This commit is contained in:
parent
f6ccc990f5
commit
6eb3089dc6
|
@ -40,7 +40,7 @@ jobs:
|
|||
# install the chromedriver corresponding to whatever version of chrome is installed
|
||||
yarn add --ignore-scripts chromedriver@^$(google-chrome --version | awk '{print $3}' | cut -d. -f1)
|
||||
PERF=1 yarn build:rollup
|
||||
./bin/setup-benchmark.sh
|
||||
yarn benchmark:runtime:setup
|
||||
|
||||
# first-load
|
||||
- name: Benchmark first-load
|
||||
|
@ -85,4 +85,34 @@ jobs:
|
|||
report-id: emoji-picker-element-database-interactions
|
||||
path: test/benchmark/database-interactions.results.json
|
||||
pr-bench-name: this-change
|
||||
base-bench-name: tip-of-tree
|
||||
|
||||
# change-tab
|
||||
- name: Benchmark change-tab
|
||||
run: |
|
||||
./node_modules/.bin/tach \
|
||||
--config ./test/benchmark/change-tab.tachometer.json \
|
||||
--json-file ./test/benchmark/change-tab.results.json
|
||||
|
||||
- name: Report change-tab
|
||||
uses: andrewiggins/tachometer-reporter-action@v2
|
||||
with:
|
||||
report-id: emoji-picker-element-change-tab
|
||||
path: test/benchmark/change-tab.results.json
|
||||
pr-bench-name: this-change
|
||||
base-bench-name: tip-of-tree
|
||||
|
||||
# search
|
||||
- name: Benchmark search
|
||||
run: |
|
||||
./node_modules/.bin/tach \
|
||||
--config ./test/benchmark/search.tachometer.json \
|
||||
--json-file ./test/benchmark/search.results.json
|
||||
|
||||
- name: Report search
|
||||
uses: andrewiggins/tachometer-reporter-action@v2
|
||||
with:
|
||||
report-id: emoji-picker-element-search
|
||||
path: test/benchmark/search.results.json
|
||||
pr-bench-name: this-change
|
||||
base-bench-name: tip-of-tree
|
|
@ -131,5 +131,8 @@ dist
|
|||
/bundle.js
|
||||
/test/benchmark/node_modules
|
||||
/test/benchmark/data.json
|
||||
/test/benchmark/*.js
|
||||
/test/benchmark/database.js
|
||||
/test/benchmark/index.js
|
||||
/test/benchmark/picker.js
|
||||
/test/benchmark/*.tachometer.json
|
||||
/i18n
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Write a bunch of *.tachometer.json files since they contain a lot of boilerplate
|
||||
import fs from 'node:fs'
|
||||
|
||||
const benchmarks = fs.readdirSync('./test/benchmark').filter(_ => _.endsWith('.benchmark.js'))
|
||||
|
||||
for (const benchmark of benchmarks) {
|
||||
const benchmarkShortName = benchmark.replace('.benchmark.js', '')
|
||||
|
||||
const content = {
|
||||
$schema: 'https://raw.githubusercontent.com/Polymer/tachometer/master/config.schema.json',
|
||||
sampleSize: 50,
|
||||
timeout: 5,
|
||||
autoSampleConditions: ['10%'],
|
||||
benchmarks: [
|
||||
{
|
||||
url: `./index.html?benchmark=${benchmarkShortName}`,
|
||||
browser: {
|
||||
name: 'chrome',
|
||||
headless: true
|
||||
},
|
||||
measurement: [
|
||||
{
|
||||
mode: 'performance',
|
||||
entryName: 'benchmark-total'
|
||||
}
|
||||
],
|
||||
expand: [
|
||||
{
|
||||
name: 'this-change'
|
||||
},
|
||||
{
|
||||
name: 'tip-of-tree',
|
||||
packageVersions: {
|
||||
label: 'tip-of-tree',
|
||||
dependencies: {
|
||||
'emoji-picker-element': {
|
||||
kind: 'git',
|
||||
repo: 'https://github.com/nolanlawson/emoji-picker-element.git',
|
||||
ref: 'master',
|
||||
setupCommands: [
|
||||
'yarn --immutable --ignore-scripts',
|
||||
'PERF=1 yarn build:rollup'
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
fs.writeFileSync(`./test/benchmark/${benchmarkShortName}.tachometer.json`, JSON.stringify(content, null, 2), 'utf-8')
|
||||
}
|
|
@ -26,7 +26,7 @@
|
|||
"build:toc": "node ./bin/generateTOC",
|
||||
"build:i18n": "node ./bin/buildI18n",
|
||||
"benchmark:runtime": "cross-env PERF=1 run-s build:rollup benchmark:runtime:setup benchmark:runtime:firstload benchmark:runtime:secondload benchmark:runtime:database",
|
||||
"benchmark:runtime:setup": "./bin/setup-benchmark.sh",
|
||||
"benchmark:runtime:setup": "./bin/setup-benchmark.sh && node ./bin/setupTachometerConfigs.js",
|
||||
"benchmark:runtime:firstload": "tach --config ./test/benchmark/first-load.tachometer.json",
|
||||
"benchmark:runtime:secondload": "tach --config ./test/benchmark/second-load.tachometer.json",
|
||||
"benchmark:runtime:database": "tach --config ./test/benchmark/database-interactions.tachometer.json",
|
||||
|
|
|
@ -1,31 +1,48 @@
|
|||
// hijack the performance.mark/measure API so we can add our own marks/measures just for the benchmark
|
||||
const { mark, measure } = performance
|
||||
|
||||
performance.mark = function (name) {
|
||||
if (name === 'initialLoad') {
|
||||
mark.call(performance, 'benchmark-start')
|
||||
}
|
||||
}
|
||||
|
||||
performance.measure = function (name, start) {
|
||||
if (name === 'initialLoad' && start === 'initialLoad') {
|
||||
// test to make sure the picker loaded with no errors
|
||||
const hasErrors = document.querySelector('emoji-picker') && document.querySelector('emoji-picker')
|
||||
.shadowRoot.querySelector('.message:not(.gone)')
|
||||
if (hasErrors) {
|
||||
console.error('picker is showing an error message')
|
||||
} else {
|
||||
measure.call(performance, 'benchmark-total', 'benchmark-start')
|
||||
function instrumentPickerLoading () {
|
||||
const observer = new PerformanceObserver(entries => {
|
||||
for (const { name, startTime, duration } of entries.getEntries()) {
|
||||
if (name === 'initialLoad') {
|
||||
// test to make sure the picker loaded with no errors
|
||||
const hasErrors = document.querySelector('emoji-picker') && document.querySelector('emoji-picker')
|
||||
.shadowRoot.querySelector('.message:not(.gone)')
|
||||
if (hasErrors) {
|
||||
console.error('picker is showing an error message')
|
||||
} else {
|
||||
performance.measure('benchmark-total', { start: startTime, duration })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
observer.observe({ entryTypes: ['measure'] })
|
||||
}
|
||||
|
||||
function useFakeEtag () {
|
||||
// Fake an eTag on the headers for the emoji-picker data so that we actually reuse the cache.
|
||||
// Tachometer doesn't serve an eTag by default
|
||||
const nativeGet = Headers.prototype.get
|
||||
Headers.prototype.get = function (name) {
|
||||
if (name.toLowerCase() === 'etag') {
|
||||
return 'W/fakeEtag'
|
||||
}
|
||||
return nativeGet.call(this, name)
|
||||
}
|
||||
}
|
||||
|
||||
// Fake an eTag on the headers for the emoji-picker data so that we actually reuse the cache.
|
||||
// Tachometer doesn't serve an eTag by default
|
||||
const nativeGet = Headers.prototype.get
|
||||
Headers.prototype.get = function (name) {
|
||||
if (name.toLowerCase() === 'etag') {
|
||||
return 'W/fakeEtag'
|
||||
}
|
||||
return nativeGet.call(this, name)
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
const benchmark = params.get('benchmark') || 'first-load'
|
||||
|
||||
if (benchmark === 'first-load') {
|
||||
instrumentPickerLoading()
|
||||
await import('./first-load.benchmark.js')
|
||||
} else if (benchmark === 'second-load') {
|
||||
instrumentPickerLoading()
|
||||
useFakeEtag()
|
||||
await import('./second-load.benchmark.js')
|
||||
} else if (benchmark === 'database-interactions') {
|
||||
await import('./database-interactions.benchmark.js')
|
||||
} else if (benchmark === 'change-tab') {
|
||||
await import('./change-tab.benchmark.js')
|
||||
} else if (benchmark === 'search') {
|
||||
await import('./search.benchmark.js')
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import Picker from './picker.js'
|
||||
import { waitForElementWithId, postRaf } from './utils.js'
|
||||
|
||||
const picker = new Picker()
|
||||
document.body.appendChild(picker)
|
||||
|
||||
await waitForElementWithId(picker.shadowRoot, 'emo-😀')
|
||||
await postRaf()
|
||||
const peopleTabButton = picker.shadowRoot.querySelector('[role="tab"][aria-label="People and body"]')
|
||||
|
||||
performance.mark('start-change-tab')
|
||||
peopleTabButton.click()
|
||||
|
||||
await waitForElementWithId(picker.shadowRoot, 'emo-👋')
|
||||
await postRaf()
|
||||
performance.measure('benchmark-total', 'start-change-tab')
|
|
@ -0,0 +1,19 @@
|
|||
import Database from './database.js'
|
||||
|
||||
performance.mark('start-db-interactions')
|
||||
const dataSource = './data.json'
|
||||
const database = new Database({ dataSource })
|
||||
await database.ready()
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await database.getEmojiByUnicodeOrName('💥')
|
||||
await database.getEmojiBySearchQuery('boom')
|
||||
await database.getEmojiByShortcode('boom')
|
||||
await database.getEmojiByGroup(1)
|
||||
await database.getPreferredSkinTone()
|
||||
await database.getTopFavoriteEmoji(10)
|
||||
await database.incrementFavoriteEmojiCount('💥')
|
||||
await database.setPreferredSkinTone(0)
|
||||
}
|
||||
|
||||
performance.measure('benchmark-total', 'start-db-interactions')
|
|
@ -1,31 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./benchmark.js"></script>
|
||||
<script type="module">
|
||||
import Database from './database.js'
|
||||
|
||||
performance.mark('initialLoad')
|
||||
const dataSource = './data.json'
|
||||
const database = new Database({ dataSource })
|
||||
await database.ready()
|
||||
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
await database.getEmojiByUnicodeOrName('💥')
|
||||
await database.getEmojiBySearchQuery('boom')
|
||||
await database.getEmojiByShortcode('boom')
|
||||
await database.getEmojiByGroup(1)
|
||||
await database.getPreferredSkinTone()
|
||||
await database.getTopFavoriteEmoji(10)
|
||||
await database.incrementFavoriteEmojiCount('💥')
|
||||
await database.setPreferredSkinTone(0)
|
||||
}
|
||||
|
||||
performance.measure('initialLoad', 'initialLoad')
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Polymer/tachometer/master/config.schema.json",
|
||||
"sampleSize": 50,
|
||||
"timeout": 5,
|
||||
"autoSampleConditions": ["10%"],
|
||||
"benchmarks": [
|
||||
{
|
||||
"url": "./database-interactions.html",
|
||||
"browser": {
|
||||
"name": "chrome",
|
||||
"headless": true
|
||||
},
|
||||
"measurement": [
|
||||
{
|
||||
"mode": "performance",
|
||||
"entryName": "benchmark-total"
|
||||
}
|
||||
],
|
||||
"expand": [
|
||||
{
|
||||
"name": "this-change"
|
||||
},
|
||||
{
|
||||
"name": "tip-of-tree",
|
||||
"packageVersions": {
|
||||
"label": "tip-of-tree",
|
||||
"dependencies": {
|
||||
"emoji-picker-element": {
|
||||
"kind": "git",
|
||||
"repo": "https://github.com/nolanlawson/emoji-picker-element.git",
|
||||
"ref": "master",
|
||||
"setupCommands": [
|
||||
"yarn --immutable --ignore-scripts",
|
||||
"PERF=1 yarn build:rollup"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import Picker from './picker.js'
|
||||
|
||||
const dataSource = './data.json'
|
||||
|
||||
document.body.appendChild(new Picker({ dataSource }))
|
|
@ -1,16 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./benchmark.js"></script>
|
||||
<script type="module">
|
||||
import Picker from './picker.js'
|
||||
|
||||
const dataSource = './data.json'
|
||||
|
||||
document.body.appendChild(new Picker({ dataSource }))
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Polymer/tachometer/master/config.schema.json",
|
||||
"sampleSize": 50,
|
||||
"timeout": 5,
|
||||
"autoSampleConditions": ["10%"],
|
||||
"benchmarks": [
|
||||
{
|
||||
"url": "./first-load.html",
|
||||
"browser": {
|
||||
"name": "chrome",
|
||||
"headless": true
|
||||
},
|
||||
"measurement": [
|
||||
{
|
||||
"mode": "performance",
|
||||
"entryName": "benchmark-total"
|
||||
}
|
||||
],
|
||||
"expand": [
|
||||
{
|
||||
"name": "this-change"
|
||||
},
|
||||
{
|
||||
"name": "tip-of-tree",
|
||||
"packageVersions": {
|
||||
"label": "tip-of-tree",
|
||||
"dependencies": {
|
||||
"emoji-picker-element": {
|
||||
"kind": "git",
|
||||
"repo": "https://github.com/nolanlawson/emoji-picker-element.git",
|
||||
"ref": "master",
|
||||
"setupCommands": [
|
||||
"yarn --immutable --ignore-scripts",
|
||||
"PERF=1 yarn build:rollup"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./benchmark.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
import Picker from './picker.js'
|
||||
import { waitForElementWithId, postRaf } from './utils.js'
|
||||
|
||||
const picker = new Picker()
|
||||
document.body.appendChild(picker)
|
||||
|
||||
await waitForElementWithId(picker.shadowRoot, 'emo-😀')
|
||||
await postRaf()
|
||||
const searchBox = picker.shadowRoot.querySelector('[role="combobox"]')
|
||||
|
||||
performance.mark('start-search')
|
||||
searchBox.value = 'fa' // "face" returns a lot of results, we want a non-trivial benchmark
|
||||
searchBox.dispatchEvent(new Event('input', { bubbles: true }))
|
||||
|
||||
await waitForElementWithId(picker.shadowRoot, 'emo-🐻')
|
||||
await postRaf()
|
||||
performance.measure('benchmark-total', 'start-search')
|
|
@ -0,0 +1,13 @@
|
|||
import Database from './database.js'
|
||||
|
||||
const dataSource = './data.json'
|
||||
|
||||
// populate IndexedDB so the Picker is just reading from the local store
|
||||
const db = new Database({ dataSource })
|
||||
await db.ready()
|
||||
await db.close()
|
||||
|
||||
// lazy-load the picker so that its logic to determine emoji support runs during the perf measure
|
||||
const { default: Picker } = await import('../../picker.js')
|
||||
|
||||
document.body.appendChild(new Picker({ dataSource }))
|
|
@ -1,26 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
</head>
|
||||
<body>
|
||||
<script type="module" src="./benchmark.js"></script>
|
||||
<script type="module">
|
||||
import Database from './database.js'
|
||||
|
||||
(async () => {
|
||||
const dataSource = './data.json'
|
||||
|
||||
// populate IndexedDB so the Picker is just reading from the local store
|
||||
const db = new Database({ dataSource })
|
||||
await db.ready()
|
||||
await db.close()
|
||||
|
||||
// lazy-load the picker so that its logic to determine emoji support runs during the perf measure
|
||||
const { default: Picker } = await import('../../picker.js')
|
||||
|
||||
document.body.appendChild(new Picker({ dataSource }))
|
||||
})()
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,43 +0,0 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/Polymer/tachometer/master/config.schema.json",
|
||||
"sampleSize": 50,
|
||||
"timeout": 5,
|
||||
"autoSampleConditions": ["10%"],
|
||||
"benchmarks": [
|
||||
{
|
||||
"url": "./second-load.html",
|
||||
"browser": {
|
||||
"name": "chrome",
|
||||
"headless": true
|
||||
},
|
||||
"measurement": [
|
||||
{
|
||||
"mode": "performance",
|
||||
"entryName": "benchmark-total"
|
||||
}
|
||||
],
|
||||
"expand": [
|
||||
{
|
||||
"name": "this-change"
|
||||
},
|
||||
{
|
||||
"name": "tip-of-tree",
|
||||
"packageVersions": {
|
||||
"label": "tip-of-tree",
|
||||
"dependencies": {
|
||||
"emoji-picker-element": {
|
||||
"kind": "git",
|
||||
"repo": "https://github.com/nolanlawson/emoji-picker-element.git",
|
||||
"ref": "master",
|
||||
"setupCommands": [
|
||||
"yarn --immutable --ignore-scripts",
|
||||
"PERF=1 yarn build:rollup"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export const raf = () => new Promise(resolve => requestAnimationFrame(resolve))
|
||||
export const postRaf = () => new Promise(resolve => requestAnimationFrame(() => setTimeout(resolve)))
|
||||
|
||||
export const waitForElementWithId = async (root, id) => {
|
||||
while (!root.getElementById(id)) {
|
||||
await raf()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue