From 136742dff7da070892b27f5235d3dcb6327d240c Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Wed, 15 Nov 2023 19:14:57 +0100 Subject: [PATCH 1/6] test: mark SEA tests as flaky on PowerPC PR-URL: https://github.com/nodejs/node/pull/50750 Refs: https://github.com/nodejs/node/issues/50740 Refs: https://github.com/nodejs/reliability/issues/718 Reviewed-By: Yagiz Nizipli Reviewed-By: Michael Dawson --- test/sequential/sequential.status | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/sequential/sequential.status b/test/sequential/sequential.status index 6d89fa0a4d2127..c25fdaa17236bd 100644 --- a/test/sequential/sequential.status +++ b/test/sequential/sequential.status @@ -46,3 +46,11 @@ test-watch-mode-inspect: PASS, FLAKY [$arch==s390x] # https://github.com/nodejs/node/issues/41286 test-performance-eventloopdelay: PASS, FLAKY + +[$arch==ppc] +# https://github.com/nodejs/node/issues/50740 +test-single-executable-application-empty: PASS, FLAKY +test-single-executable-application-snapshot-and-code-cache: PASS, FLAKY +test-single-executable-application-snapshot: PASS, FLAKY +test-single-executable-application-use-code-cache: PASS, FLAKY +test-single-executable-application: PASS, FLAKY From e5189cbf7e40f1d099f7ac49a36509b9bd265e1e Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Sat, 18 Nov 2023 04:52:00 +0900 Subject: [PATCH 2/6] build: fix GN configuration for deps/base64 PR-URL: https://github.com/nodejs/node/pull/50696 Reviewed-By: Luigi Pinca Reviewed-By: Marco Ippolito Reviewed-By: Jiawen Geng --- deps/base64/unofficial.gni | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/deps/base64/unofficial.gni b/deps/base64/unofficial.gni index 8138d88798e1df..269c62d976d6ad 100644 --- a/deps/base64/unofficial.gni +++ b/deps/base64/unofficial.gni @@ -32,6 +32,7 @@ template("base64_gn_build") { "HAVE_SSE42=1", "HAVE_AVX=1", "HAVE_AVX2=1", + "HAVE_AVX512=1", ] } if (target_cpu == "arm") { @@ -65,6 +66,7 @@ template("base64_gn_build") { ":base64_sse42", ":base64_avx", ":base64_avx2", + ":base64_avx512", ":base64_neon32", ":base64_neon64", ] @@ -111,6 +113,7 @@ template("base64_gn_build") { } } } + source_set("base64_avx2") { configs += [ ":base64_internal_config" ] sources = [ "base64/lib/arch/avx2/codec.c" ] @@ -123,6 +126,21 @@ template("base64_gn_build") { } } + source_set("base64_avx512") { + configs += [ ":base64_internal_config" ] + sources = [ "base64/lib/arch/avx512/codec.c" ] + if (target_cpu == "x86" || target_cpu == "x64") { + if (is_clang || !is_win) { + cflags_c = [ + "-mavx512vl", + "-mavx512vbmi", + ] + } else if (is_win) { + cflags_c = [ "/arch:AVX512" ] + } + } + } + source_set("base64_neon32") { configs += [ ":base64_internal_config" ] sources = [ "base64/lib/arch/neon32/codec.c" ] From ce4642a5c2ac3b184de873a53818f624ea29152b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 17 Nov 2023 21:00:30 +0100 Subject: [PATCH 3/6] test: skip parallel/test-macos-app-sandbox if disk space < 120MB It needs to copy the Node.js binary which is currently almost 100MB. To be safe, skip the test when the available disk space is smaller than 120MB. PR-URL: https://github.com/nodejs/node/pull/50764 Reviewed-By: Debadree Chatterjee Reviewed-By: Yagiz Nizipli Reviewed-By: Luigi Pinca --- test/parallel/test-macos-app-sandbox.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/parallel/test-macos-app-sandbox.js b/test/parallel/test-macos-app-sandbox.js index 91485b75983f84..60ad67b3db3790 100644 --- a/test/parallel/test-macos-app-sandbox.js +++ b/test/parallel/test-macos-app-sandbox.js @@ -17,6 +17,10 @@ const nodeBinary = process.execPath; tmpdir.refresh(); +if (!tmpdir.hasEnoughSpace(120 * 1024 * 1024)) { + common.skip('Available disk space < 120MB'); +} + const appBundlePath = tmpdir.resolve('node_sandboxed.app'); const appBundleContentPath = path.join(appBundlePath, 'Contents'); const appExecutablePath = path.join( From 0e24f91457a68204398e3cf44f8d04c7f01c11a3 Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Fri, 17 Nov 2023 22:42:21 +0100 Subject: [PATCH 4/6] test: improve test-bootstrap-modules.js Divide builtins into two lists depending on whether they are loaded before pre-execution or at run time, and give clearer suggestions about how to deal with them based on the category they are in. This helps preventing regressions like the one reported in https://github.com/nodejs/node/issues/45662. PR-URL: https://github.com/nodejs/node/pull/50708 Reviewed-By: Antoine du Hamel Reviewed-By: Richard Lau Reviewed-By: Yagiz Nizipli Reviewed-By: Chengzhong Wu --- test/parallel/test-bootstrap-modules.js | 128 +++++++++++++++++++----- 1 file changed, 103 insertions(+), 25 deletions(-) diff --git a/test/parallel/test-bootstrap-modules.js b/test/parallel/test-bootstrap-modules.js index e123c190329ba6..d158e489196c39 100644 --- a/test/parallel/test-bootstrap-modules.js +++ b/test/parallel/test-bootstrap-modules.js @@ -1,14 +1,27 @@ -// Flags: --expose-internals 'use strict'; -// This list must be computed before we require any modules to +// This list must be computed before we require any builtins to // to eliminate the noise. -const actualModules = new Set(process.moduleLoadList.slice()); +const list = process.moduleLoadList.slice(); const common = require('../common'); const assert = require('assert'); +const { inspect } = require('util'); -const expectedModules = new Set([ +const preExecIndex = + list.findIndex((i) => i.includes('pre_execution')); +const actual = { + beforePreExec: new Set(list.slice(0, preExecIndex)), + atRunTime: new Set(list.slice(preExecIndex)), +}; + +// Currently, we don't add additional builtins to worker snapshots. +// So for worker snapshots we'll just concatenate the two. Once we +// add more builtins to worker snapshots, we should also distinguish +// the two stages for them. +const expected = {}; + +expected.beforePreExec = new Set([ 'Internal Binding builtins', 'Internal Binding encoding_binding', 'Internal Binding modules', @@ -85,22 +98,25 @@ const expectedModules = new Set([ 'NativeModule internal/modules/package_json_reader', 'Internal Binding module_wrap', 'NativeModule internal/modules/cjs/loader', - 'NativeModule internal/vm/module', - 'NativeModule internal/modules/esm/utils', +]); + +expected.atRunTime = new Set([ 'Internal Binding wasm_web_api', 'Internal Binding worker', 'NativeModule internal/modules/run_main', 'NativeModule internal/net', 'NativeModule internal/dns/utils', 'NativeModule internal/process/pre_execution', + 'NativeModule internal/vm/module', + 'NativeModule internal/modules/esm/utils', ]); if (common.isMainThread) { [ 'NativeModule internal/idna', 'NativeModule url', - ].forEach(expectedModules.add.bind(expectedModules)); -} else { + ].forEach(expected.beforePreExec.add.bind(expected.beforePreExec)); +} else { // Worker. [ 'NativeModule diagnostics_channel', 'NativeModule internal/abort_controller', @@ -128,35 +144,97 @@ if (common.isMainThread) { 'NativeModule stream/promises', 'NativeModule string_decoder', 'NativeModule worker_threads', - ].forEach(expectedModules.add.bind(expectedModules)); + ].forEach(expected.atRunTime.add.bind(expected.atRunTime)); + // For now we'll concatenate the two stages for workers. We prefer + // atRunTime here because that's what currently happens for these. } if (common.isWindows) { // On Windows fs needs SideEffectFreeRegExpPrototypeExec which uses vm. - expectedModules.add('NativeModule vm'); + expected.atRunTime.add('NativeModule vm'); } if (common.hasIntl) { - expectedModules.add('Internal Binding icu'); + expected.beforePreExec.add('Internal Binding icu'); } if (process.features.inspector) { - expectedModules.add('Internal Binding inspector'); - expectedModules.add('NativeModule internal/inspector_async_hook'); - expectedModules.add('NativeModule internal/util/inspector'); + expected.beforePreExec.add('Internal Binding inspector'); + expected.beforePreExec.add('NativeModule internal/util/inspector'); + expected.atRunTime.add('NativeModule internal/inspector_async_hook'); } const difference = (setA, setB) => { return new Set([...setA].filter((x) => !setB.has(x))); }; -const missingModules = difference(expectedModules, actualModules); -const extraModules = difference(actualModules, expectedModules); -const printSet = (s) => { return `${[...s].sort().join(',\n ')}\n`; }; - -assert.deepStrictEqual(actualModules, expectedModules, - (missingModules.size > 0 ? - 'These modules were not loaded:\n ' + - printSet(missingModules) : '') + - (extraModules.size > 0 ? - 'These modules were unexpectedly loaded:\n ' + - printSet(extraModules) : '')); + +// Accumulate all the errors and print them at the end instead of throwing +// immediately which makes it harder to update the test. +const errorLogs = []; +function err(message) { + if (typeof message === 'string') { + errorLogs.push(message); + } else { + // Show the items in individual lines for easier copy-pasting. + errorLogs.push(inspect(message, { compact: false })); + } +} + +if (common.isMainThread) { + const missing = difference(expected.beforePreExec, actual.beforePreExec); + const extra = difference(actual.beforePreExec, expected.beforePreExec); + if (missing.size !== 0) { + err('These builtins are now no longer loaded before pre-execution.'); + err('If this is intentional, remove them from `expected.beforePreExec`.'); + err('\n--- These could be removed from expected.beforePreExec ---'); + err([...missing].sort()); + err(''); + } + if (extra.size !== 0) { + err('These builtins are now unexpectedly loaded before pre-execution.'); + err('If this is intentional, add them to `expected.beforePreExec`.'); + err('\n# Note: loading more builtins before pre-execution can lead to ' + + 'startup performance regression or invalid snapshots.'); + err('- Consider lazy loading builtins that are not used universally.'); + err('- Make sure that the builtins do not access environment dependent ' + + 'states e.g. command line arguments or environment variables ' + + 'during loading.'); + err('- When in doubt, ask @nodejs/startup.'); + err('\n--- These could be added to expected.beforePreExec ---'); + err([...extra].sort()); + err(''); + } +} + +if (!common.isMainThread) { + // For workers, just merge beforePreExec into atRunTime for now. + // When we start adding modules to the worker snapshot, this branch + // can be removed and we can just remove the common.isMainThread + // conditions. + expected.beforePreExec.forEach(expected.atRunTime.add.bind(expected.atRunTime)); + actual.beforePreExec.forEach(actual.atRunTime.add.bind(actual.atRunTime)); +} + +{ + const missing = difference(expected.atRunTime, actual.atRunTime); + const extra = difference(actual.atRunTime, expected.atRunTime); + if (missing.size !== 0) { + err('These builtins are now no longer loaded at run time.'); + err('If this is intentional, remove them from `expected.atRunTime`.'); + err('\n--- These could be removed from expected.atRunTime ---'); + err([...missing].sort()); + err(''); + } + if (extra.size !== 0) { + err('These builtins are now unexpectedly loaded at run time.'); + err('If this is intentional, add them to `expected.atRunTime`.'); + err('\n# Note: loading more builtins at run time can lead to ' + + 'startup performance regression.'); + err('- Consider lazy loading builtins that are not used universally.'); + err('\n--- These could be added to expected.atRunTime ---'); + err([...extra].sort()); + err(''); + } +} + +assert.strictEqual(errorLogs.length, 0, errorLogs.join('\n')); From 8c7fe471fb1569aa989a8036e42d6b4dc66b871d Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 18 Nov 2023 00:59:26 +0100 Subject: [PATCH 5/6] perf_hooks: implement performance.now() with fast API calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/50492 Reviewed-By: Vinícius Lourenço Claro Cardoso Reviewed-By: Yagiz Nizipli Reviewed-By: Bryan English Reviewed-By: Chengzhong Wu Reviewed-By: Benjamin Gruenbaum Reviewed-By: Stephen Belanger --- benchmark/perf_hooks/now.js | 23 +++++++++++++++++++++++ lib/internal/perf/utils.js | 7 +------ src/node_external_reference.h | 3 +++ src/node_perf.cc | 21 +++++++++++++++++++++ 4 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 benchmark/perf_hooks/now.js diff --git a/benchmark/perf_hooks/now.js b/benchmark/perf_hooks/now.js new file mode 100644 index 00000000000000..7132196c430125 --- /dev/null +++ b/benchmark/perf_hooks/now.js @@ -0,0 +1,23 @@ +'use strict'; + +const assert = require('assert'); +const common = require('../common.js'); + +const bench = common.createBenchmark(main, { + n: [1e6], +}); + +function main({ n }) { + const arr = []; + for (let i = 0; i < n; ++i) { + arr.push(performance.now()); + } + + bench.start(); + for (let i = 0; i < n; i++) { + arr[i] = performance.now(); + } + bench.end(n); + + assert.strictEqual(arr.length, n); +} diff --git a/lib/internal/perf/utils.js b/lib/internal/perf/utils.js index a0b7955c70481c..bbc1c996e318f8 100644 --- a/lib/internal/perf/utils.js +++ b/lib/internal/perf/utils.js @@ -5,6 +5,7 @@ const { NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN, }, milestones, + now, } = internalBinding('performance'); function getTimeOrigin() { @@ -13,12 +14,6 @@ function getTimeOrigin() { return milestones[NODE_PERFORMANCE_MILESTONE_TIME_ORIGIN] / 1e6; } -// Returns the time relative to the process start time in milliseconds. -function now() { - const hr = process.hrtime(); - return (hr[0] * 1000 + hr[1] / 1e6) - getTimeOrigin(); -} - // Returns the milestone relative to the process start time in milliseconds. function getMilestoneTimestamp(milestoneIdx) { const ns = milestones[milestoneIdx]; diff --git a/src/node_external_reference.h b/src/node_external_reference.h index a647967077967e..f15c97c03d2e9d 100644 --- a/src/node_external_reference.h +++ b/src/node_external_reference.h @@ -15,6 +15,8 @@ using CFunctionCallbackWithOneByteString = using CFunctionCallback = void (*)(v8::Local receiver); using CFunctionCallbackReturnDouble = double (*)(v8::Local receiver); +using CFunctionCallbackValueReturnDouble = + double (*)(v8::Local receiver); using CFunctionCallbackWithInt64 = void (*)(v8::Local receiver, int64_t); using CFunctionCallbackWithBool = void (*)(v8::Local receiver, @@ -38,6 +40,7 @@ class ExternalReferenceRegistry { V(CFunctionCallback) \ V(CFunctionCallbackWithOneByteString) \ V(CFunctionCallbackReturnDouble) \ + V(CFunctionCallbackValueReturnDouble) \ V(CFunctionCallbackWithInt64) \ V(CFunctionCallbackWithBool) \ V(CFunctionCallbackWithString) \ diff --git a/src/node_perf.cc b/src/node_perf.cc index 360cc8bf673073..603af57b2639b2 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -291,6 +291,22 @@ void MarkBootstrapComplete(const FunctionCallbackInfo& args) { performance::NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE); } +static double PerformanceNowImpl() { + return static_cast(uv_hrtime() - performance_process_start) / + NANOS_PER_MILLIS; +} + +static double FastPerformanceNow(v8::Local receiver) { + return PerformanceNowImpl(); +} + +static void SlowPerformanceNow(const FunctionCallbackInfo& args) { + args.GetReturnValue().Set(PerformanceNowImpl()); +} + +static v8::CFunction fast_performance_now( + v8::CFunction::Make(FastPerformanceNow)); + static void CreatePerIsolateProperties(IsolateData* isolate_data, Local target) { Isolate* isolate = isolate_data->isolate(); @@ -311,6 +327,8 @@ static void CreatePerIsolateProperties(IsolateData* isolate_data, SetMethod(isolate, target, "getTimeOriginTimestamp", GetTimeOriginTimeStamp); SetMethod(isolate, target, "createELDHistogram", CreateELDHistogram); SetMethod(isolate, target, "markBootstrapComplete", MarkBootstrapComplete); + SetFastMethodNoSideEffect( + isolate, target, "now", SlowPerformanceNow, &fast_performance_now); } void CreatePerContextProperties(Local target, @@ -376,6 +394,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(GetTimeOriginTimeStamp); registry->Register(CreateELDHistogram); registry->Register(MarkBootstrapComplete); + registry->Register(SlowPerformanceNow); + registry->Register(FastPerformanceNow); + registry->Register(fast_performance_now.GetTypeInfo()); HistogramBase::RegisterExternalReferences(registry); IntervalHistogram::RegisterExternalReferences(registry); } From 4e23d6904c74c2fcaeef293a7fde86c6517fd76b Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 18 Nov 2023 01:46:42 +0100 Subject: [PATCH 6/6] benchmark: rewrite import.meta benchmark MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a ESM benchmark, rewrite it so that we are directly benchmarking the ESM import.meta paths and using number of loads for op/s calculation, instead of doing it in startup benchmarks and nesting number of process/workers spawn for op/s calculation. PR-URL: https://github.com/nodejs/node/pull/50683 Reviewed-By: James M Snell Reviewed-By: Vinícius Lourenço Claro Cardoso --- benchmark/esm/import-meta.js | 32 +++++++++++++++++++++++++ benchmark/fixtures/esm-dir-file.mjs | 5 ++-- benchmark/fixtures/load-esm-dir-file.js | 5 ---- benchmark/misc/startup.js | 1 - 4 files changed, 34 insertions(+), 9 deletions(-) create mode 100644 benchmark/esm/import-meta.js delete mode 100644 benchmark/fixtures/load-esm-dir-file.js diff --git a/benchmark/esm/import-meta.js b/benchmark/esm/import-meta.js new file mode 100644 index 00000000000000..fd371cf60ed6a3 --- /dev/null +++ b/benchmark/esm/import-meta.js @@ -0,0 +1,32 @@ +'use strict'; + +const path = require('path'); +const { pathToFileURL, fileURLToPath } = require('url'); +const common = require('../common'); +const assert = require('assert'); +const bench = common.createBenchmark(main, { + n: [1000], +}); + +const file = pathToFileURL( + path.resolve(__filename, '../../fixtures/esm-dir-file.mjs'), +); +async function load(array, n) { + for (let i = 0; i < n; i++) { + array[i] = await import(`${file}?i=${i}`); + } + return array; +} + +function main({ n }) { + const array = []; + for (let i = 0; i < n; ++i) { + array.push({ dirname: '', filename: '', i: 0 }); + } + + bench.start(); + load(array, n).then((arr) => { + bench.end(n); + assert.strictEqual(arr[n - 1].filename, fileURLToPath(file)); + }); +} diff --git a/benchmark/fixtures/esm-dir-file.mjs b/benchmark/fixtures/esm-dir-file.mjs index d5dafc5c46697e..a1028d57afd56b 100644 --- a/benchmark/fixtures/esm-dir-file.mjs +++ b/benchmark/fixtures/esm-dir-file.mjs @@ -1,3 +1,2 @@ -import assert from 'assert'; -assert.ok(import.meta.dirname); -assert.ok(import.meta.filename); +export const dirname = import.meta.dirname; +export const filename = import.meta.filename; diff --git a/benchmark/fixtures/load-esm-dir-file.js b/benchmark/fixtures/load-esm-dir-file.js deleted file mode 100644 index a4115dd39e0489..00000000000000 --- a/benchmark/fixtures/load-esm-dir-file.js +++ /dev/null @@ -1,5 +0,0 @@ -(async function () { - for (let i = 0; i < 1000; i += 1) { - await import(`./esm-dir-file.mjs?i=${i}`); - } -}()); diff --git a/benchmark/misc/startup.js b/benchmark/misc/startup.js index f55be1a7902e4f..07c0701d128899 100644 --- a/benchmark/misc/startup.js +++ b/benchmark/misc/startup.js @@ -9,7 +9,6 @@ const bench = common.createBenchmark(main, { script: [ 'benchmark/fixtures/require-builtins', 'test/fixtures/semicolon', - 'benchmark/fixtures/load-esm-dir-file', ], mode: ['process', 'worker'], count: [30],