Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

src: make --use-largepages a runtime option #30954

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
33 changes: 0 additions & 33 deletions configure.py
Expand Up @@ -404,17 +404,6 @@
dest='with_etw',
help='build with ETW (default is true on Windows)')

parser.add_option('--use-largepages',
action='store_true',
dest='node_use_large_pages',
help='build with Large Pages support. This feature is supported only on Linux kernel' +
'>= 2.6.38 with Transparent Huge pages enabled and FreeBSD')

parser.add_option('--use-largepages-script-lld',
action='store_true',
dest='node_use_large_pages_script_lld',
help='link against the LLVM ld linker script. Implies -fuse-ld=lld in the linker flags')

intl_optgroup.add_option('--with-intl',
action='store',
dest='with_intl',
Expand Down Expand Up @@ -1068,28 +1057,6 @@ def configure_node(o):
else:
o['variables']['node_use_dtrace'] = 'false'

if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'):
raise Exception(
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'):
if options.shared or options.enable_static:
raise Exception(
'Large pages are supported only while creating node executable.')
if target_arch!="x64":
raise Exception(
'Large pages are supported only x64 platform.')
if flavor == 'mac':
info('macOS server with 32GB or more is recommended')
if flavor == 'linux':
# Example full version string: 2.6.32-696.28.1.el6.x86_64
FULL_KERNEL_VERSION=os.uname()[2]
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
if KERNEL_VERSION < "2.6.38" and flavor == 'linux':
raise Exception(
'Large pages need Linux kernel version >= 2.6.38')
o['variables']['node_use_large_pages'] = b(options.node_use_large_pages)
o['variables']['node_use_large_pages_script_lld'] = b(options.node_use_large_pages_script_lld)

if options.no_ifaddrs:
o['defines'] += ['SUNOS_NO_IFADDRS']

Expand Down
17 changes: 17 additions & 0 deletions doc/api/cli.md
Expand Up @@ -863,6 +863,22 @@ environment variables.

See `SSL_CERT_DIR` and `SSL_CERT_FILE`.

### `--use-largepages=num`
<!-- YAML
added: REPLACEME
-->

Re-map the Node.js static code to large memory pages at startup. If supported on
the target system, this will cause the Node.js static code to be moved onto 2
MiB pages instead of 4 KiB pages.

The following values are valid for `num`:
* 0: No mapping will be attempted. This is the default.
* 1: If supported by the OS, mapping will be attempted. Failure to map will be
ignored and will not be reported.
* 2: If supported by the OS, mapping will be attempted. Failure to map will be
ignored and a message will be printed to standard error.

### `--v8-options`
<!-- YAML
added: v0.1.3
Expand Down Expand Up @@ -1119,6 +1135,7 @@ Node.js options that are allowed are:
* `--track-heap-objects`
* `--unhandled-rejections`
* `--use-bundled-ca`
* `--use-largepages`
* `--use-openssl-ca`
* `--v8-pool-size`
* `--zero-fill-buffers`
Expand Down
7 changes: 7 additions & 0 deletions doc/node.1
Expand Up @@ -398,6 +398,13 @@ See
and
.Ev SSL_CERT_FILE .
.
.It Fl -use-largepages Ns = Ns Ar num
Re-map the Node.js static code to large memory pages at startup. If supported on
the target system, this will cause the Node.js static code to be moved onto 2
MiB pages instead of 4 KiB pages.
.Pp
The following values are valid: 0 (do not map), 1 (map, ignore failure, and do not report it), and 2 (map, ignore failure, but report it to standard error).
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
.
.It Fl -v8-options
Print V8 command-line options.
.
Expand Down
2 changes: 1 addition & 1 deletion node.gyp
Expand Up @@ -833,7 +833,7 @@
}],
],
}],
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
[ 'OS in "linux freebsd mac"', {
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
# The current implementation of Large Pages is under Linux.
# Other implementations are possible but not currently supported.
Expand Down
6 changes: 2 additions & 4 deletions node.gypi
Expand Up @@ -308,17 +308,15 @@
}],
[ 'OS=="linux" and '
'target_arch=="x64" and '
'node_use_large_pages=="true" and '
'node_use_large_pages_script_lld=="false"', {
'llvm_version=="0.0"', {
'ldflags': [
'-Wl,-T',
'<!(realpath src/large_pages/ld.implicit.script)',
]
}],
[ 'OS=="linux" and '
'target_arch=="x64" and '
'node_use_large_pages=="true" and '
'node_use_large_pages_script_lld=="true"', {
'llvm_version!="0.0"', {
'ldflags': [
'-Wl,-T',
'<!(realpath src/large_pages/ld.implicit.script.lld)',
Expand Down
6 changes: 3 additions & 3 deletions src/large_pages/node_large_page.cc
Expand Up @@ -62,7 +62,7 @@
// Map a new area and copy the original code there
// Use mmap using the start address with MAP_FIXED so we get exactly the
// same virtual address
// Use madvise with MADV_HUGE_PAGE to use Anonymous 2M Pages
// Use madvise with MADV_HUGEPAGE to use Anonymous 2M Pages
// If successful copy the code there and unmap the original region.

extern char __nodetext;
Expand Down Expand Up @@ -308,7 +308,7 @@ static bool IsSuperPagesEnabled() {
// a. map a new area and copy the original code there
// b. mmap using the start address with MAP_FIXED so we get exactly
// the same virtual address (except on macOS).
// c. madvise with MADV_HUGE_PAGE
// c. madvise with MADV_HUGEPAGE
// d. If successful copy the code there and unmap the original region
int
#if !defined(__APPLE__)
Expand Down Expand Up @@ -352,7 +352,7 @@ MoveTextRegionToLargePages(const text_region& r) {
return -1;
}

ret = madvise(tmem, size, MADV_HUGEPAGE);
ret = madvise(tmem, size, 14 /* MADV_HUGEPAGE */);
devnexen marked this conversation as resolved.
Show resolved Hide resolved
if (ret == -1) {
PrintSystemError(errno);
ret = munmap(tmem, size);
Expand Down
20 changes: 10 additions & 10 deletions src/node.cc
Expand Up @@ -64,9 +64,7 @@
#include "inspector/worker_inspector.h" // ParentInspectorHandle
#endif

#ifdef NODE_ENABLE_LARGE_CODE_PAGES
#include "large_pages/node_large_page.h"
#endif

#ifdef NODE_REPORT
#include "node_report.h"
Expand Down Expand Up @@ -967,14 +965,6 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {

CHECK_GT(argc, 0);

#ifdef NODE_ENABLE_LARGE_CODE_PAGES
if (node::IsLargePagesEnabled()) {
if (node::MapStaticCodeToLargePages() != 0) {
fprintf(stderr, "Reverting to default page size\n");
}
}
#endif

// Hack around with the argv pointer. Used for process.title = "blah".
argv = uv_setup_args(argc, argv);

Expand All @@ -994,6 +984,16 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
}
}

if (per_process::cli_options->use_largepages == 1 ||
per_process::cli_options->use_largepages == 2) {
if (node::IsLargePagesEnabled()) {
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
if (node::MapStaticCodeToLargePages() != 0 &&
per_process::cli_options->use_largepages == 2) {
fprintf(stderr, "Reverting to default page size\n");
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

if (per_process::cli_options->print_version) {
printf("%s\n", NODE_VERSION);
result.exit_code = 0;
Expand Down
7 changes: 7 additions & 0 deletions src/node_options.cc
Expand Up @@ -63,6 +63,9 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) {
"used, not both");
}
#endif
if (!(use_largepages >= 0 && use_largepages <= 2)) {
errors->push_back("--use-largepages must be one of 0, 1, or 2");
}
per_isolate->CheckOptions(errors);
}

Expand Down Expand Up @@ -748,6 +751,10 @@ PerProcessOptionsParser::PerProcessOptionsParser(
kAllowedInEnvironment);
#endif
#endif
AddOption("--use-largepages",
lundibundi marked this conversation as resolved.
Show resolved Hide resolved
"Map the Node.js static code to large pages",
&PerProcessOptions::use_largepages,
kAllowedInEnvironment);

Insert(iop, &PerProcessOptions::get_per_isolate_options);
}
Expand Down
1 change: 1 addition & 0 deletions src/node_options.h
Expand Up @@ -235,6 +235,7 @@ class PerProcessOptions : public Options {
bool force_fips_crypto = false;
#endif
#endif
uint64_t use_largepages = 0;

#ifdef NODE_REPORT
std::vector<std::string> cmdline;
Expand Down
31 changes: 31 additions & 0 deletions test/parallel/test-startup-large-pages.js
@@ -0,0 +1,31 @@
'use strict';

// A sanity check: Make sure that Node.js runs correctly when running with the
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
// --use-largepages option.

require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');

{
const child = spawnSync(process.execPath,
[ '--use-largepages=1', '-p', '42' ]);
const stdout = child.stdout.toString().match(/\S+/g);
assert.strictEqual(child.status, 0);
assert.strictEqual(child.signal, null);
assert.strictEqual(stdout.length, 1);
assert.strictEqual(stdout[0], '42');
}

{
const child = spawnSync(process.execPath,
[ '--use-largepages=3', '-p', '42' ]);
assert.strictEqual(child.status, 9);
assert.strictEqual(child.signal, null);
assert.strictEqual(child.stderr.toString().match(/\S+/g).slice(1).join(' '),
'--use-largepages must be one of 0, 1, or 2');
}

// TODO(gabrielschulhof): Run with --use-largepages=2 and make assertions about
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
// the stderr, which may or may not contain a message indicating that mapping to
// large pages has failed.