Tracking render performance


#1

Hey folks,

I want to track the performance of my site to get a baseline for improvements. I would do this via Analytics but I have the problem that I can not track the actual cost of aurelia. I’m able to track until the data is in the view model but as there can be quite a big dom as result of the data passed in it’s possible that the rendered result is visible over one second later to the user as the data was injected into the view.

I’ve already had a look at the event aggregator if there are any events published. Do you know any possibility to track when aurelia finished updating the expressions?

Thanks in advance

Best
FragSalat


#2

Correct me if I am wrong.
Aurelia rendering is done by flushing micro task queue (an array of rendering tasks), You can monkey patch TaskQueue implementation to set a timer check.

queueMicroTask(task: Task | Function): void {
  if (this.microTaskQueue.length < 1) {
// record start time here
    this.requestFlushMicroTaskQueue();
  }

  if (this.longStacks) {
    task.stack = this.prepareQueueStack(microStackSeparator);
  }

  this.microTaskQueue.push(task);
}

flushMicroTaskQueue(): void {
  let queue = this.microTaskQueue;
  this._flushQueue(queue, this.microTaskQueueCapacity);
  queue.length = 0;
// record end time here, then get consumed time of rendering
}

#3

Indeed this is a very hacky but possible solution. One problem I have here is that the queues are flushed multiple times and I would have to catch the last one. So this will be the problem in this case. I’ll analyze it if I could do a timeout on flushing and if it hits I track the last recorded time.


#4

FYI: This solution worked until now quite good. I’m now able to track when the page rendered. Sometimes there might be another render event fired because of late evaluated binding behaviors but this is catchable with a variable to only track it once. Keep in mind that the timeout add’s 1s to the tracked time which must be removed. Here is my solution.

/**
 * Extend original function with additional functionality
 * @param {Object} proto
 * @param {string} fnName
 * @param {Function} callback
 */
export function extendFn(proto: Object, fnName: string, callback: Function): void {
  const origFn = proto[fnName];
  proto[fnName] = function extendedFn() {
    let result;
    if (typeof origFn === 'function') {
      result = origFn.apply(this, arguments);
    }
    callback.apply(this, arguments);
    return result;
  };
}

/**
 * Hook into micro task queue to track when rendering is finished
 * @param {TaskQueue} taskQueue
 * @param {EventAggregator} events
 */
function hookRendering(taskQueue: TaskQueue, events: EventAggregator): void {
  let timeoutHandle;
  extendFn(taskQueue, 'flushMicroTaskQueue', () => {
    if (timeoutHandle) {
      clearTimeout(timeoutHandle);
    }
    const actual = performance.now();
    timeoutHandle = setTimeout(() => {
      events.publish('app.rendered', performance.now() - actual);
    }, 1000);
  });
}

The actual page load time can be tracked this way:

let initialPageLoad = true;
events.subscribe('app.rendered', delta => {
  if (initialPageLoad) {
    initialPageLoad = false;
    log.debug(`Page loaded in ${performance.now() - delta}ms`);
  }
})

#5

why not use lighthouse for something like this?
you get a nice report.
using puppeteer/npm module of lighthouse will make it easier to save results/compare


#6

I think lighthouse is just for local performance measurement but not for enduser performance measurement. This means that PC’s which are not as good as mine have worse load times.


#7

Lighthouse emulates a nexus 5x/fast 3g.
Best way to test preformance will be have 2-3 last verions of your site on a test server and run 2-5 tests using average.
Using the npm/puppeteer module you should be able to automate this.

Or maybe I just missunderstood the goal.
Im no expert in performance testing so ofc I could be wrong :slight_smile: