Skip to content

Commit

Permalink
async_hooks: add async local storage propagation benchmarks
Browse files Browse the repository at this point in the history
Add micro-benchmarks to verify the performance degradation related to
the number of active `AsyncLocalStorage`s.

With these benchmarks, trying to improve the async context propagation
to be an O(1) operation, which is an operation more frequent compared
to `asyncLocalStorage.run` and `asyncLocalStorage.getStore`.

PR-URL: #46414
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Gerhard Stöbich <deb2001-github@yahoo.de>
Reviewed-By: Minwoo Jung <nodecorelab@gmail.com>
  • Loading branch information
legendecas authored and MylesBorins committed Feb 18, 2023
1 parent 683a1f8 commit 731a7ae
Show file tree
Hide file tree
Showing 4 changed files with 186 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';
const common = require('../common.js');
const { AsyncLocalStorage, AsyncResource } = require('async_hooks');

/**
* This benchmark verifies the performance of
* `AsyncLocalStorage.getStore()` on propagation through async
* resource scopes.
*
* - AsyncLocalStorage.run()
* - AsyncResource.runInAsyncScope
* - AsyncResource.runInAsyncScope
* ...
* - AsyncResource.runInAsyncScope
* - AsyncLocalStorage.getStore()
*/
const bench = common.createBenchmark(main, {
resourceCount: [10, 100, 1000],
n: [1e4],
});

function runBenchmark(store, n) {
for (let i = 0; i < n; i++) {
store.getStore();
}
}

function runInAsyncScopes(resourceCount, cb, i = 0) {
if (i === resourceCount) {
cb();
} else {
const resource = new AsyncResource('noop');
resource.runInAsyncScope(() => {
runInAsyncScopes(resourceCount, cb, i + 1);
});
}
}

function main({ n, resourceCount }) {
const store = new AsyncLocalStorage();
runInAsyncScopes(resourceCount, () => {
bench.start();
runBenchmark(store, n);
bench.end(n);
});
}
46 changes: 46 additions & 0 deletions benchmark/async_hooks/async-local-storage-getstore-nested-run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';
const common = require('../common.js');
const { AsyncLocalStorage } = require('async_hooks');

/**
* This benchmark verifies the performance of
* `AsyncLocalStorage.getStore()` on multiple `AsyncLocalStorage` instances
* nested `AsyncLocalStorage.run()`s.
*
* - AsyncLocalStorage1.run()
* - AsyncLocalStorage2.run()
* ...
* - AsyncLocalStorageN.run()
* - AsyncLocalStorage1.getStore()
*/
const bench = common.createBenchmark(main, {
sotrageCount: [1, 10, 100],
n: [1e4],
});

function runBenchmark(store, n) {
for (let idx = 0; idx < n; idx++) {
store.getStore();
}
}

function runStores(stores, value, cb, idx = 0) {
if (idx === stores.length) {
cb();
} else {
stores[idx].run(value, () => {
runStores(stores, value, cb, idx + 1);
});
}
}

function main({ n, sotrageCount }) {
const stores = new Array(sotrageCount).fill(0).map(() => new AsyncLocalStorage());
const contextValue = {};

runStores(stores, contextValue, () => {
bench.start();
runBenchmark(stores[0], n);
bench.end(n);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
'use strict';
const common = require('../common.js');
const { AsyncLocalStorage, AsyncResource } = require('async_hooks');

/**
* This benchmark verifies the performance degradation of
* async resource propagation on the increasing number of
* active `AsyncLocalStorage`s.
*
* - AsyncLocalStorage.run() * storageCount
* - new AsyncResource()
* - new AsyncResource()
* ...
* - N new Asyncresource()
*/
const bench = common.createBenchmark(main, {
storageCount: [0, 1, 10, 100],
n: [1e3],
});

function runStores(stores, value, cb, idx = 0) {
if (idx === stores.length) {
cb();
} else {
stores[idx].run(value, () => {
runStores(stores, value, cb, idx + 1);
});
}
}

function runBenchmark(n) {
for (let i = 0; i < n; i++) {
new AsyncResource('noop');
}
}

function main({ n, storageCount }) {
const stores = new Array(storageCount).fill(0).map(() => new AsyncLocalStorage());
const contextValue = {};

runStores(stores, contextValue, () => {
bench.start();
runBenchmark(n);
bench.end(n);
});
}
48 changes: 48 additions & 0 deletions benchmark/async_hooks/async-local-storage-propagate-promise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';
const common = require('../common.js');
const { AsyncLocalStorage } = require('async_hooks');

/**
* This benchmark verifies the performance degradation of
* async resource propagation on the increasing number of
* active `AsyncLocalStorage`s.
*
* - AsyncLocalStorage.run()
* - Promise
* - Promise
* ...
* - Promise
*/
const bench = common.createBenchmark(main, {
storageCount: [0, 1, 10, 100],
n: [1e5],
});

function runStores(stores, value, cb, idx = 0) {
if (idx === stores.length) {
cb();
} else {
stores[idx].run(value, () => {
runStores(stores, value, cb, idx + 1);
});
}
}

async function runBenchmark(n) {
for (let i = 0; i < n; i++) {
// Avoid creating additional ticks.
await undefined;
}
}

function main({ n, storageCount }) {
const stores = new Array(storageCount).fill(0).map(() => new AsyncLocalStorage());
const contextValue = {};

runStores(stores, contextValue, () => {
bench.start();
runBenchmark(n).then(() => {
bench.end(n);
});
});
}

0 comments on commit 731a7ae

Please sign in to comment.