mitata icon indicating copy to clipboard operation
mitata copied to clipboard

Feature: before / after bench hooks?

Open smeijer opened this issue 3 years ago • 4 comments

Sometimes when we bench some code, we need to prepare the environment. Think of a scenario like when we try to compare the performance of two different database indexes. The baseline can be a query without index, and two bench statements both with their own unique index. Pseudo:

group('select 10 users', () => {
  baseline('no index', async () => {
    await client.query('select * from users order by created_at asc, id asc limit 10');
  });

  bench('with created_at index', async() => {
    await client.query(`select * from users order by created_at asc, id asc limit 10`);
  }, { 
    before: async () => dropIndexes().then(() => createFirstIndex()),
    after: async () => dropIndexes(),
  });

  bench('with created_at and id index', async() => {
    await client.query(`select * from users order by created_at asc, id asc limit 10`);
  }, { 
    before: async () => dropIndexes().then(() => createAnotherIndex()),
    after: async () => dropIndexes(),
  });
});

I think it would be useful to have it both on the group level, as well as on the bench level. The group could in this case for example be used to connect/disconnect to the database, or to support multiple query tests for a single created index, where another group would test another index.

smeijer avatar Nov 03 '22 16:11 smeijer

I think a pretty solution - better than the above - could be to add a prepare handler that runs in order of the benchmarks, but only once. Something like:

group('select 10 users', () => {
  prepare('clean', async () => clean());
  baseline('no index', async () => client.query(...));

  prepare('create index 1', async () => clean() && createIndex1());
  bench('select 10 users using index 1', async () => client.query(...));

  prepare('create index 2', async () => clean() && createIndex2());
  bench('select 10 users using index 2', async () => client.query(...));

  prepare('cleanup', async () => clean());
});

smeijer avatar Nov 03 '22 16:11 smeijer

It's something that I am aware of and trying to solve with 1.0.0 release to simplify everything I'm leaning towards generators and for loops design

bench(function *() {
  // prep work
  const index = ...;

  yield () => {
    // benchmark code
  }

  // cleanup work
  index.deinit();
})

// and

bench(ctx => {
  // prep

  for (const _ of ctx) {
    // benchmarked code
  }

  // cleanup
})

evanwashere avatar Jan 24 '24 12:01 evanwashere

@evanwashere I'd like to add a small +1 in favour of the generator design as it looks like it would really nicely separate out the prep and cleanup from the timing of the actual code being benchmarked.

Plus supporting async generators would allow nice async setup/teardown in combination with synchronous benchmarked code:

bench(async function *() {
  // prep work
  const index = await ...;

  yield () => {
    // benchmark code
  }

  // cleanup work
  await index.deinit();
})

willfrew avatar Jan 31 '24 09:01 willfrew

The generator proposal looks nice! I suggest closing #13 🙂

smeijer avatar Feb 02 '24 13:02 smeijer