When working on client-side work in Discourse core/plugins/themes, it’s important to consider the performance impact. Google’s ‘Tachometer’ project provides a statistically rigorous benchmarking tool which we can use to definitively measure the effect of changes.
Essentially, this tool takes a list of URLs and loads them in a ‘round-robin’ fashion. For each page load, it takes some performance measurement. After hundreds/thousands of iterations, it produces a comparison table.
The beauty of this ‘round-robin’ approach is that it helps to reduce the impact of external factors on measurements.
Step 1: Add performance.measure()
The approach here will vary based on what you’re testing. But fundamentally: you need to introduce a performance.measure()
value for Tachometer to read.
If you want to render the time it takes for Discourse to boot and render, you can use the built-in “discourse-init-to-paint” measurement. For anything else, you can introduce your own performance.measure
and use that.
You can check it’s working using the performance tab in your browser dev tools:
If you’re trying to measure an activity which requires user interaction (e.g. opening a menu), you could achieve this by adding something like this in an initializer to click the button 1 second after the page is loaded:
setTimeout(() => document.querySelector(".my-button").click(), 1000)
Step 2: Identify URLs for testing
Firstly, make sure that you are building Ember assets in production mode. This can be achieved by launching the server with EMBER_ENV=production
.
To obtain two different URLs, there are two main approaches:
If your change is small enough to be easily feature-flagged, then you could add logic to toggle it based on a URL query parameter. Then your two urls could be
http://localhost:4200?flag=before
http://localhost:4200?flag=after
If the change is too large for that, then you could clone Discourse into a second directory and launch a second copy of ember-cli. It can be proxied to the same Rails server using a command like
EMBER_ENV=production pnpm ember serve --port 4201 --proxy http://localhost:3000
And then your two URLs would be
http://localhost:4200
http://localhost:4201
If you take this approach, make sure that both copies of the app have the performance telemetry you introduced in step 1 of this guide
Step 3: Configure Tachometer
This is my bench.json
file, which will take 300 samples of each target:
{
"timeout": 5,
"sampleSize": 300,
"benchmarks": [
{
"measurement": {
"mode": "performance",
"entryName": "discourse-init-to-paint"
},
"expand": [
{
"url": "http://localhost:4200",
"name": "before"
},
{
"url": "http://localhost:4201",
"name": "after"
}
]
}
]
}
Step 4: Run benchmark
To reduce noise, stop any unrelated activities on your workstation, and then start the benchmark with a command like:
npx tachometer@latest --config ./bench.json
When complete, you should see a comparison of the before/after performance.
Caveats
As with any experiments like this, it’s worth considering the limitations. For example:
-
Performance differences on your development workstation may not map directly onto other browsers/devices
-
The boot process of Discourse through the Ember-CLI proxy is not exactly the same as it is in production. When making structural changes (e.g. framework updates) this may be important
-
Performance often varies based on application state (e.g. the number of topics being rendered), so your results may not be exactly reproducible in other environments
This document is version controlled - suggest changes on github.