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 all 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=mode`
<!-- 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 `mode`:
* `off`: No mapping will be attempted. This is the default.
* `on`: If supported by the OS, mapping will be attempted. Failure to map will
be ignored and a message will be printed to standard error.
* `silent`: If supported by the OS, mapping will be attempted. Failure to map
will be ignored and will not be reported.

### `--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
10 changes: 10 additions & 0 deletions doc/node.1
Expand Up @@ -398,6 +398,16 @@ See
and
.Ev SSL_CERT_FILE .
.
.It Fl -use-largepages Ns = Ns Ar mode
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
.Ar mode
must have one of the following values:
`off` (the default value, meaning do not map), `on` (map and ignore failure,
reporting it to stderr), or `silent` (map and silently ignore failure).
.
.It Fl -v8-options
Print V8 command-line options.
.
Expand Down
5 changes: 2 additions & 3 deletions node.gyp
Expand Up @@ -833,10 +833,9 @@
}],
],
}],
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
[ 'OS in "linux freebsd mac" and '
'target_arch=="x64"', {
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
# The current implementation of Large Pages is under Linux.
# Other implementations are possible but not currently supported.
'sources': [
'src/large_pages/node_large_page.cc',
'src/large_pages/node_large_page.h'
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
19 changes: 11 additions & 8 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 All @@ -333,9 +333,6 @@ MoveTextRegionToLargePages(const text_region& r) {
PrintSystemError(errno);
return -1;
}
auto munmap_on_return = OnScopeLeave([nmem, size]() {
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
});

memcpy(nmem, r.from, size);

Expand All @@ -352,13 +349,14 @@ 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);
if (ret == -1) {
PrintSystemError(errno);
}
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
return -1;
}
memcpy(start, nmem, size);
Expand All @@ -369,6 +367,7 @@ MoveTextRegionToLargePages(const text_region& r) {
MAP_ALIGNED_SUPER, -1 , 0);
if (tmem == MAP_FAILED) {
PrintSystemError(errno);
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
return -1;
}
#elif defined(__APPLE__)
Expand All @@ -383,6 +382,7 @@ MoveTextRegionToLargePages(const text_region& r) {
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
if (tmem == MAP_FAILED) {
PrintSystemError(errno);
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
return -1;
}
memcpy(tmem, nmem, size);
Expand All @@ -393,6 +393,7 @@ MoveTextRegionToLargePages(const text_region& r) {
if (ret == -1) {
PrintSystemError(errno);
}
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
return -1;
}
memcpy(start, tmem, size);
Expand All @@ -405,8 +406,10 @@ MoveTextRegionToLargePages(const text_region& r) {
if (ret == -1) {
PrintSystemError(errno);
}
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
return -1;
}
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
return ret;
}

Expand All @@ -418,12 +421,12 @@ int MapStaticCodeToLargePages() {
return -1;
}

#if defined(__linux__)
#if defined(__linux__) || defined(__FreeBSD__)
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
return MoveTextRegionToLargePages(r);

return -1;
#elif defined(__FreeBSD__) || defined(__APPLE__)
#elif defined(__APPLE__)
return MoveTextRegionToLargePages(r);
#endif
}
Expand Down
30 changes: 20 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,26 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
}
}

#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
if (per_process::cli_options->use_largepages == "on" ||
per_process::cli_options->use_largepages == "silent") {
if (node::IsLargePagesEnabled()) {
gabrielschulhof marked this conversation as resolved.
Show resolved Hide resolved
if (node::MapStaticCodeToLargePages() != 0 &&
per_process::cli_options->use_largepages != "silent") {
fprintf(stderr,
"Mapping code to large pages failed. Reverting to default page "
"size.\n");
}
} else if (per_process::cli_options->use_largepages != "silent") {
fprintf(stderr, "Large pages are not enabled.\n");
}
}
#else
if (per_process::cli_options->use_largepages == "on") {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoops! This condition has to be == "on" and not != "silent" otherwise it complains on all systems where it's not supported 😳

fprintf(stderr, "Mapping to large pages is not supported.\n");
}
#endif // NODE_ENABLE_LARGE_CODE_PAGES

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

Expand Down Expand Up @@ -748,6 +753,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
std::string use_largepages = "off";

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

// Make sure that Node.js runs correctly with the --use-largepages option.

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

{
const child = spawnSync(process.execPath,
[ '--use-largepages=on', '-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=xyzzy', '-p', '42' ]);
assert.strictEqual(child.status, 9);
assert.strictEqual(child.signal, null);
assert.strictEqual(child.stderr.toString().match(/\S+/g).slice(1).join(' '),
'invalid value for --use-largepages');
}

// TODO(gabrielschulhof): Make assertions about the stderr, which may or may not
// contain a message indicating that mapping to large pages has failed.