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], 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/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" ] 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); } 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')); 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( 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