Open Bug 1978670 Opened 29 days ago Updated 22 days ago

Create microbenchmark for mozjexl and optimize

Categories

(Core :: JavaScript Engine, task, P3)

task

Tracking

()

People

(Reporter: mgaudet, Unassigned)

References

(Blocks 3 open bugs)

Details

One of the tasks happening in applink startup is executing JEXL expressions, https://github.com/mozilla/mozjexl

https://share.firefox.dev/40yRs8y suggests ~70ms in the parent process.

mozjexl is in tree; we could write a microbenchmark to investigate if we could speed it up.

The most important step being that we need to get some insight into the JEXL expressions being executed.

A basic outline is:

/**
 * Quick performance test for mozjexl
 * Run with: ./mach run -m mozjexl-perf-quick.js from the firefox root. 
 */


import { mozjexl } from './toolkit/components/utils/mozjexl.sys.mjs';

const jexl = new mozjexl.Jexl();

// Add test transforms
jexl.addTransform('upper', str => str.toUpperCase());
jexl.addTransform('length', val => val.length);

// Test data
const context = {
  simple: 42,
  nested: { value: 100 },
  employees: [
    { name: 'Alice', age: 30, salary: 70000 },
    { name: 'Bob', age: 35, salary: 80000 },
    { name: 'Charlie', age: 25, salary: 60000 },
    { name: 'David', age: 40, salary: 90000 },
    { name: 'Eve', age: 28, salary: 65000 }
  ]
};

// Test cases
const tests = [
  { name: 'Simple arithmetic', expr: '2 + 3 * 4' },
  { name: 'Variable access', expr: 'simple' },
  { name: 'Nested access', expr: 'nested.value' },
  { name: 'Array filter', expr: 'employees[.age > 30]' },
  { name: 'Transform', expr: '"hello"|upper' },
  { name: 'Complex', expr: 'employees[.age > 30]|length > 1' }
];

// Run tests
async function runTests() {
  print('mozjexl Quick Performance Test\n');
  
  for (const test of tests) {
    // Warmup
    for (let i = 0; i < 5; i++) {
      await jexl.eval(test.expr, context);
    }
    
    // Measure
    const times = [];
    const iterations = 50;
    
    for (let i = 0; i < iterations; i++) {
      const start = dateNow();
      await jexl.eval(test.expr, context);
      const end = dateNow();
      times.push(end - start);
    }
    
    const avg = times.reduce((a, b) => a + b) / times.length;
    print(`${test.name}: ${avg.toFixed(3)}ms (${test.expr})`);
  }
}

runTests().then(
  () => { print('\nDone'); quit(0); },
  err => { print('Error:', err); quit(1); }
);

Do you know where one could get a corpus of 'representative' jexl that you'd expect to see, so we could rig up a benchmark?

Flags: needinfo?(brennie)

Thats' great actually;

I will say, writing a microbenchmark here is awkwrd though, because basically since jexl.eval returns a promise, it's hard for this to not turn into a promises microbenchmark. T

It should be a promises microbenchmark. That's where all the time is being spent.

That being said we are also trying this approach: https://github.com/mozilla/mozjexl/commit/14f7f16f83d3629f203640c934c87d26762711bf

Here's a profile of synchronous-promises/benchmark.html
https://share.firefox.dev/4f7hghR

This has the promise overhead mostly eliminated. But mozjexl is still pretty slow. It does not seem designed to run quickly.

We should honestly consider rewriting the evaluator in moxjexl to be smarter about only awaiting when necessary.

There has not been a vendoring of mozjexl into the tree in a long time though and i believe the version in tree is different from the version on github.

Flags: needinfo?(brennie)
Severity: -- → S3
Priority: -- → P3
You need to log in before you can comment on or make changes to this bug.