Skip to content

Commit 6dcb868

Browse files
Gabriel Schulhofdevnexen
authored andcommitted
src: make --use-largepages a runtime option
Moves the option that instructs Node.js to-remap its static code to large pages from a configure-time option to a runtime option. This should make it easy to assess the performance impact of such a change without having to custom-build. Backport-PR-URL: #32092 PR-URL: #30954 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: David Carlier <[email protected]> Reviewed-By: David Carlier <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: Denys Otrishko <[email protected]> Co-authored-by: David Carlier <[email protected]>
1 parent 3241aee commit 6dcb868

File tree

10 files changed

+101
-58
lines changed

10 files changed

+101
-58
lines changed

configure.py

-33
Original file line numberDiff line numberDiff line change
@@ -424,17 +424,6 @@
424424
dest='with_etw',
425425
help='build with ETW (default is true on Windows)')
426426

427-
parser.add_option('--use-largepages',
428-
action='store_true',
429-
dest='node_use_large_pages',
430-
help='build with Large Pages support. This feature is supported only on Linux kernel' +
431-
'>= 2.6.38 with Transparent Huge pages enabled and FreeBSD')
432-
433-
parser.add_option('--use-largepages-script-lld',
434-
action='store_true',
435-
dest='node_use_large_pages_script_lld',
436-
help='link against the LLVM ld linker script. Implies -fuse-ld=lld in the linker flags')
437-
438427
intl_optgroup.add_option('--with-intl',
439428
action='store',
440429
dest='with_intl',
@@ -1104,28 +1093,6 @@ def configure_node(o):
11041093
else:
11051094
o['variables']['node_use_dtrace'] = 'false'
11061095

1107-
if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'):
1108-
raise Exception(
1109-
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
1110-
if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'):
1111-
if options.shared or options.enable_static:
1112-
raise Exception(
1113-
'Large pages are supported only while creating node executable.')
1114-
if target_arch!="x64":
1115-
raise Exception(
1116-
'Large pages are supported only x64 platform.')
1117-
if flavor == 'mac':
1118-
info('macOS server with 32GB or more is recommended')
1119-
if flavor == 'linux':
1120-
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1121-
FULL_KERNEL_VERSION=os.uname()[2]
1122-
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1123-
if KERNEL_VERSION < "2.6.38" and flavor == 'linux':
1124-
raise Exception(
1125-
'Large pages need Linux kernel version >= 2.6.38')
1126-
o['variables']['node_use_large_pages'] = b(options.node_use_large_pages)
1127-
o['variables']['node_use_large_pages_script_lld'] = b(options.node_use_large_pages_script_lld)
1128-
11291096
if options.no_ifaddrs:
11301097
o['defines'] += ['SUNOS_NO_IFADDRS']
11311098

doc/api/cli.md

+17
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,22 @@ environment variables.
907907

908908
See `SSL_CERT_DIR` and `SSL_CERT_FILE`.
909909

910+
### `--use-largepages=mode`
911+
<!-- YAML
912+
added: REPLACEME
913+
-->
914+
915+
Re-map the Node.js static code to large memory pages at startup. If supported on
916+
the target system, this will cause the Node.js static code to be moved onto 2
917+
MiB pages instead of 4 KiB pages.
918+
919+
The following values are valid for `mode`:
920+
* `off`: No mapping will be attempted. This is the default.
921+
* `on`: If supported by the OS, mapping will be attempted. Failure to map will
922+
be ignored and a message will be printed to standard error.
923+
* `silent`: If supported by the OS, mapping will be attempted. Failure to map
924+
will be ignored and will not be reported.
925+
910926
### `--v8-options`
911927
<!-- YAML
912928
added: v0.1.3
@@ -1165,6 +1181,7 @@ Node.js options that are allowed are:
11651181
* `--track-heap-objects`
11661182
* `--unhandled-rejections`
11671183
* `--use-bundled-ca`
1184+
* `--use-largepages`
11681185
* `--use-openssl-ca`
11691186
* `--v8-pool-size`
11701187
* `--zero-fill-buffers`

doc/node.1

+10
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,16 @@ See
434434
and
435435
.Ev SSL_CERT_FILE .
436436
.
437+
.It Fl -use-largepages Ns = Ns Ar mode
438+
Re-map the Node.js static code to large memory pages at startup. If supported on
439+
the target system, this will cause the Node.js static code to be moved onto 2
440+
MiB pages instead of 4 KiB pages.
441+
.Pp
442+
.Ar mode
443+
must have one of the following values:
444+
`off` (the default value, meaning do not map), `on` (map and ignore failure,
445+
reporting it to stderr), or `silent` (map and silently ignore failure).
446+
.
437447
.It Fl -v8-options
438448
Print V8 command-line options.
439449
.

node.gyp

+2-3
Original file line numberDiff line numberDiff line change
@@ -853,10 +853,9 @@
853853
}],
854854
],
855855
}],
856-
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
856+
[ 'OS in "linux freebsd mac" and '
857+
'target_arch=="x64"', {
857858
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
858-
# The current implementation of Large Pages is under Linux.
859-
# Other implementations are possible but not currently supported.
860859
'sources': [
861860
'src/large_pages/node_large_page.cc',
862861
'src/large_pages/node_large_page.h'

node.gypi

+2-4
Original file line numberDiff line numberDiff line change
@@ -319,17 +319,15 @@
319319
}],
320320
[ 'OS=="linux" and '
321321
'target_arch=="x64" and '
322-
'node_use_large_pages=="true" and '
323-
'node_use_large_pages_script_lld=="false"', {
322+
'llvm_version=="0.0"', {
324323
'ldflags': [
325324
'-Wl,-T',
326325
'<!(realpath src/large_pages/ld.implicit.script)',
327326
]
328327
}],
329328
[ 'OS=="linux" and '
330329
'target_arch=="x64" and '
331-
'node_use_large_pages=="true" and '
332-
'node_use_large_pages_script_lld=="true"', {
330+
'llvm_version!="0.0"', {
333331
'ldflags': [
334332
'-Wl,-T',
335333
'<!(realpath src/large_pages/ld.implicit.script.lld)',

src/large_pages/node_large_page.cc

+11-8
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
// Map a new area and copy the original code there
6363
// Use mmap using the start address with MAP_FIXED so we get exactly the
6464
// same virtual address
65-
// Use madvise with MADV_HUGE_PAGE to use Anonymous 2M Pages
65+
// Use madvise with MADV_HUGEPAGE to use Anonymous 2M Pages
6666
// If successful copy the code there and unmap the original region.
6767

6868
extern char __nodetext;
@@ -295,7 +295,7 @@ static bool IsSuperPagesEnabled() {
295295
// a. map a new area and copy the original code there
296296
// b. mmap using the start address with MAP_FIXED so we get exactly
297297
// the same virtual address (except on macOS).
298-
// c. madvise with MADV_HUGE_PAGE
298+
// c. madvise with MADV_HUGEPAGE
299299
// d. If successful copy the code there and unmap the original region
300300
int
301301
#if !defined(__APPLE__)
@@ -320,9 +320,6 @@ MoveTextRegionToLargePages(const text_region& r) {
320320
PrintSystemError(errno);
321321
return -1;
322322
}
323-
OnScopeLeave munmap_on_return([nmem, size]() {
324-
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
325-
});
326323

327324
memcpy(nmem, r.from, size);
328325

@@ -339,13 +336,14 @@ MoveTextRegionToLargePages(const text_region& r) {
339336
return -1;
340337
}
341338

342-
ret = madvise(tmem, size, MADV_HUGEPAGE);
339+
ret = madvise(tmem, size, 14 /* MADV_HUGEPAGE */);
343340
if (ret == -1) {
344341
PrintSystemError(errno);
345342
ret = munmap(tmem, size);
346343
if (ret == -1) {
347344
PrintSystemError(errno);
348345
}
346+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
349347
return -1;
350348
}
351349
memcpy(start, nmem, size);
@@ -356,6 +354,7 @@ MoveTextRegionToLargePages(const text_region& r) {
356354
MAP_ALIGNED_SUPER, -1 , 0);
357355
if (tmem == MAP_FAILED) {
358356
PrintSystemError(errno);
357+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
359358
return -1;
360359
}
361360
#elif defined(__APPLE__)
@@ -370,6 +369,7 @@ MoveTextRegionToLargePages(const text_region& r) {
370369
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
371370
if (tmem == MAP_FAILED) {
372371
PrintSystemError(errno);
372+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
373373
return -1;
374374
}
375375
memcpy(tmem, nmem, size);
@@ -380,6 +380,7 @@ MoveTextRegionToLargePages(const text_region& r) {
380380
if (ret == -1) {
381381
PrintSystemError(errno);
382382
}
383+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
383384
return -1;
384385
}
385386
memcpy(start, tmem, size);
@@ -392,8 +393,10 @@ MoveTextRegionToLargePages(const text_region& r) {
392393
if (ret == -1) {
393394
PrintSystemError(errno);
394395
}
396+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
395397
return -1;
396398
}
399+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
397400
return ret;
398401
}
399402

@@ -405,12 +408,12 @@ int MapStaticCodeToLargePages() {
405408
return -1;
406409
}
407410

408-
#if defined(__linux__)
411+
#if defined(__linux__) || defined(__FreeBSD__)
409412
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
410413
return MoveTextRegionToLargePages(r);
411414

412415
return -1;
413-
#elif defined(__FreeBSD__) || defined(__APPLE__)
416+
#elif defined(__APPLE__)
414417
return MoveTextRegionToLargePages(r);
415418
#endif
416419
}

src/node.cc

+20-10
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,7 @@
6565
#include "inspector/worker_inspector.h" // ParentInspectorHandle
6666
#endif
6767

68-
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
6968
#include "large_pages/node_large_page.h"
70-
#endif
7169

7270
#ifdef NODE_REPORT
7371
#include "node_report.h"
@@ -872,14 +870,6 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
872870

873871
CHECK_GT(argc, 0);
874872

875-
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
876-
if (node::IsLargePagesEnabled()) {
877-
if (node::MapStaticCodeToLargePages() != 0) {
878-
fprintf(stderr, "Reverting to default page size\n");
879-
}
880-
}
881-
#endif
882-
883873
// Hack around with the argv pointer. Used for process.title = "blah".
884874
argv = uv_setup_args(argc, argv);
885875

@@ -899,6 +889,26 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
899889
}
900890
}
901891

892+
#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
893+
if (per_process::cli_options->use_largepages == "on" ||
894+
per_process::cli_options->use_largepages == "silent") {
895+
if (node::IsLargePagesEnabled()) {
896+
if (node::MapStaticCodeToLargePages() != 0 &&
897+
per_process::cli_options->use_largepages != "silent") {
898+
fprintf(stderr,
899+
"Mapping code to large pages failed. Reverting to default page "
900+
"size.\n");
901+
}
902+
} else if (per_process::cli_options->use_largepages != "silent") {
903+
fprintf(stderr, "Large pages are not enabled.\n");
904+
}
905+
}
906+
#else
907+
if (per_process::cli_options->use_largepages == "on") {
908+
fprintf(stderr, "Mapping to large pages is not supported.\n");
909+
}
910+
#endif // NODE_ENABLE_LARGE_CODE_PAGES
911+
902912
if (per_process::cli_options->print_version) {
903913
printf("%s\n", NODE_VERSION);
904914
result.exit_code = 0;

src/node_options.cc

+9
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) {
6666
"used, not both");
6767
}
6868
#endif
69+
if (use_largepages != "off" &&
70+
use_largepages != "on" &&
71+
use_largepages != "silent") {
72+
errors->push_back("invalid value for --use-largepages");
73+
}
6974
per_isolate->CheckOptions(errors);
7075
}
7176

@@ -801,6 +806,10 @@ PerProcessOptionsParser::PerProcessOptionsParser(
801806
kAllowedInEnvironment);
802807
#endif
803808
#endif
809+
AddOption("--use-largepages",
810+
"Map the Node.js static code to large pages",
811+
&PerProcessOptions::use_largepages,
812+
kAllowedInEnvironment);
804813

805814
// v12.x backwards compat flags removed in V8 7.9.
806815
AddOption("--fast_calls_with_arguments_mismatches", "", NoOp{});

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ class PerProcessOptions : public Options {
239239
bool force_fips_crypto = false;
240240
#endif
241241
#endif
242+
std::string use_largepages = "off";
242243
bool trace_sigint = false;
243244

244245
#ifdef NODE_REPORT
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
// Make sure that Node.js runs correctly with the --use-largepages option.
4+
5+
require('../common');
6+
const assert = require('assert');
7+
const { spawnSync } = require('child_process');
8+
9+
{
10+
const child = spawnSync(process.execPath,
11+
[ '--use-largepages=on', '-p', '42' ]);
12+
const stdout = child.stdout.toString().match(/\S+/g);
13+
assert.strictEqual(child.status, 0);
14+
assert.strictEqual(child.signal, null);
15+
assert.strictEqual(stdout.length, 1);
16+
assert.strictEqual(stdout[0], '42');
17+
}
18+
19+
{
20+
const child = spawnSync(process.execPath,
21+
[ '--use-largepages=xyzzy', '-p', '42' ]);
22+
assert.strictEqual(child.status, 9);
23+
assert.strictEqual(child.signal, null);
24+
assert.strictEqual(child.stderr.toString().match(/\S+/g).slice(1).join(' '),
25+
'invalid value for --use-largepages');
26+
}
27+
28+
// TODO(gabrielschulhof): Make assertions about the stderr, which may or may not
29+
// contain a message indicating that mapping to large pages has failed.

0 commit comments

Comments
 (0)