From 731a7ae9da37cae684a3a345fa4df49d5e0a4fab Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sat, 4 Feb 2023 05:12:17 +0800 Subject: [PATCH] async_hooks: add async local storage propagation benchmarks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: https://github.com/nodejs/node/pull/46414 Reviewed-By: James M Snell Reviewed-By: Gerhard Stöbich Reviewed-By: Minwoo Jung --- ...local-storage-getstore-nested-resources.js | 46 ++++++++++++++++++ ...async-local-storage-getstore-nested-run.js | 46 ++++++++++++++++++ ...c-local-storage-propagate-asyncresource.js | 46 ++++++++++++++++++ .../async-local-storage-propagate-promise.js | 48 +++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 benchmark/async_hooks/async-local-storage-getstore-nested-resources.js create mode 100644 benchmark/async_hooks/async-local-storage-getstore-nested-run.js create mode 100644 benchmark/async_hooks/async-local-storage-propagate-asyncresource.js create mode 100644 benchmark/async_hooks/async-local-storage-propagate-promise.js diff --git a/benchmark/async_hooks/async-local-storage-getstore-nested-resources.js b/benchmark/async_hooks/async-local-storage-getstore-nested-resources.js new file mode 100644 index 00000000000000..05b43af627a79e --- /dev/null +++ b/benchmark/async_hooks/async-local-storage-getstore-nested-resources.js @@ -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); + }); +} diff --git a/benchmark/async_hooks/async-local-storage-getstore-nested-run.js b/benchmark/async_hooks/async-local-storage-getstore-nested-run.js new file mode 100644 index 00000000000000..5f8948f62eb13e --- /dev/null +++ b/benchmark/async_hooks/async-local-storage-getstore-nested-run.js @@ -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); + }); +} diff --git a/benchmark/async_hooks/async-local-storage-propagate-asyncresource.js b/benchmark/async_hooks/async-local-storage-propagate-asyncresource.js new file mode 100644 index 00000000000000..e5523452af6e97 --- /dev/null +++ b/benchmark/async_hooks/async-local-storage-propagate-asyncresource.js @@ -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); + }); +} diff --git a/benchmark/async_hooks/async-local-storage-propagate-promise.js b/benchmark/async_hooks/async-local-storage-propagate-promise.js new file mode 100644 index 00000000000000..8d6241d076681f --- /dev/null +++ b/benchmark/async_hooks/async-local-storage-propagate-promise.js @@ -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); + }); + }); +}