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. PR-URL: https://github.com/nodejs/node/pull/30954 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Denys Otrishko <shishugi@gmail.com> Co-authored-by: David Carlier <devnexen@gmail.com>
This commit is contained in:
parent
312f3086c2
commit
8952105e57
33
configure.py
33
configure.py
@ -404,17 +404,6 @@ parser.add_option('--with-etw',
|
|||||||
dest='with_etw',
|
dest='with_etw',
|
||||||
help='build with ETW (default is true on Windows)')
|
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',
|
intl_optgroup.add_option('--with-intl',
|
||||||
action='store',
|
action='store',
|
||||||
dest='with_intl',
|
dest='with_intl',
|
||||||
@ -1068,28 +1057,6 @@ def configure_node(o):
|
|||||||
else:
|
else:
|
||||||
o['variables']['node_use_dtrace'] = 'false'
|
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:
|
if options.no_ifaddrs:
|
||||||
o['defines'] += ['SUNOS_NO_IFADDRS']
|
o['defines'] += ['SUNOS_NO_IFADDRS']
|
||||||
|
|
||||||
|
@ -876,6 +876,22 @@ environment variables.
|
|||||||
|
|
||||||
See `SSL_CERT_DIR` and `SSL_CERT_FILE`.
|
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`
|
### `--v8-options`
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
added: v0.1.3
|
added: v0.1.3
|
||||||
@ -1133,6 +1149,7 @@ Node.js options that are allowed are:
|
|||||||
* `--track-heap-objects`
|
* `--track-heap-objects`
|
||||||
* `--unhandled-rejections`
|
* `--unhandled-rejections`
|
||||||
* `--use-bundled-ca`
|
* `--use-bundled-ca`
|
||||||
|
* `--use-largepages`
|
||||||
* `--use-openssl-ca`
|
* `--use-openssl-ca`
|
||||||
* `--v8-pool-size`
|
* `--v8-pool-size`
|
||||||
* `--zero-fill-buffers`
|
* `--zero-fill-buffers`
|
||||||
|
10
doc/node.1
10
doc/node.1
@ -402,6 +402,16 @@ See
|
|||||||
and
|
and
|
||||||
.Ev SSL_CERT_FILE .
|
.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
|
.It Fl -v8-options
|
||||||
Print V8 command-line options.
|
Print V8 command-line options.
|
||||||
.
|
.
|
||||||
|
5
node.gyp
5
node.gyp
@ -836,10 +836,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' ],
|
'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': [
|
'sources': [
|
||||||
'src/large_pages/node_large_page.cc',
|
'src/large_pages/node_large_page.cc',
|
||||||
'src/large_pages/node_large_page.h'
|
'src/large_pages/node_large_page.h'
|
||||||
|
@ -308,8 +308,7 @@
|
|||||||
}],
|
}],
|
||||||
[ 'OS=="linux" and '
|
[ 'OS=="linux" and '
|
||||||
'target_arch=="x64" and '
|
'target_arch=="x64" and '
|
||||||
'node_use_large_pages=="true" and '
|
'llvm_version=="0.0"', {
|
||||||
'node_use_large_pages_script_lld=="false"', {
|
|
||||||
'ldflags': [
|
'ldflags': [
|
||||||
'-Wl,-T',
|
'-Wl,-T',
|
||||||
'<!(realpath src/large_pages/ld.implicit.script)',
|
'<!(realpath src/large_pages/ld.implicit.script)',
|
||||||
@ -317,8 +316,7 @@
|
|||||||
}],
|
}],
|
||||||
[ 'OS=="linux" and '
|
[ 'OS=="linux" and '
|
||||||
'target_arch=="x64" and '
|
'target_arch=="x64" and '
|
||||||
'node_use_large_pages=="true" and '
|
'llvm_version!="0.0"', {
|
||||||
'node_use_large_pages_script_lld=="true"', {
|
|
||||||
'ldflags': [
|
'ldflags': [
|
||||||
'-Wl,-T',
|
'-Wl,-T',
|
||||||
'<!(realpath src/large_pages/ld.implicit.script.lld)',
|
'<!(realpath src/large_pages/ld.implicit.script.lld)',
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
// Map a new area and copy the original code there
|
// Map a new area and copy the original code there
|
||||||
// Use mmap using the start address with MAP_FIXED so we get exactly the
|
// Use mmap using the start address with MAP_FIXED so we get exactly the
|
||||||
// same virtual address
|
// 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.
|
// If successful copy the code there and unmap the original region.
|
||||||
|
|
||||||
extern char __nodetext;
|
extern char __nodetext;
|
||||||
@ -308,7 +308,7 @@ static bool IsSuperPagesEnabled() {
|
|||||||
// a. map a new area and copy the original code there
|
// a. map a new area and copy the original code there
|
||||||
// b. mmap using the start address with MAP_FIXED so we get exactly
|
// b. mmap using the start address with MAP_FIXED so we get exactly
|
||||||
// the same virtual address (except on macOS).
|
// 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
|
// d. If successful copy the code there and unmap the original region
|
||||||
int
|
int
|
||||||
#if !defined(__APPLE__)
|
#if !defined(__APPLE__)
|
||||||
@ -333,9 +333,6 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
auto munmap_on_return = OnScopeLeave([nmem, size]() {
|
|
||||||
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
|
||||||
});
|
|
||||||
|
|
||||||
memcpy(nmem, r.from, size);
|
memcpy(nmem, r.from, size);
|
||||||
|
|
||||||
@ -352,13 +349,14 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = madvise(tmem, size, MADV_HUGEPAGE);
|
ret = madvise(tmem, size, 14 /* MADV_HUGEPAGE */);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
ret = munmap(tmem, size);
|
ret = munmap(tmem, size);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
}
|
}
|
||||||
|
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(start, nmem, size);
|
memcpy(start, nmem, size);
|
||||||
@ -369,6 +367,7 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
MAP_ALIGNED_SUPER, -1 , 0);
|
MAP_ALIGNED_SUPER, -1 , 0);
|
||||||
if (tmem == MAP_FAILED) {
|
if (tmem == MAP_FAILED) {
|
||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
|
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
@ -383,6 +382,7 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
|
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
|
||||||
if (tmem == MAP_FAILED) {
|
if (tmem == MAP_FAILED) {
|
||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
|
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(tmem, nmem, size);
|
memcpy(tmem, nmem, size);
|
||||||
@ -393,6 +393,7 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
}
|
}
|
||||||
|
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(start, tmem, size);
|
memcpy(start, tmem, size);
|
||||||
@ -405,8 +406,10 @@ MoveTextRegionToLargePages(const text_region& r) {
|
|||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
PrintSystemError(errno);
|
PrintSystemError(errno);
|
||||||
}
|
}
|
||||||
|
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,12 +421,12 @@ int MapStaticCodeToLargePages() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__linux__)
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
|
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
|
||||||
return MoveTextRegionToLargePages(r);
|
return MoveTextRegionToLargePages(r);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
#elif defined(__FreeBSD__) || defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
return MoveTextRegionToLargePages(r);
|
return MoveTextRegionToLargePages(r);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
30
src/node.cc
30
src/node.cc
@ -64,9 +64,7 @@
|
|||||||
#include "inspector/worker_inspector.h" // ParentInspectorHandle
|
#include "inspector/worker_inspector.h" // ParentInspectorHandle
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
|
|
||||||
#include "large_pages/node_large_page.h"
|
#include "large_pages/node_large_page.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NODE_REPORT
|
#ifdef NODE_REPORT
|
||||||
#include "node_report.h"
|
#include "node_report.h"
|
||||||
@ -1000,14 +998,6 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv) {
|
|||||||
|
|
||||||
CHECK_GT(argc, 0);
|
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".
|
// Hack around with the argv pointer. Used for process.title = "blah".
|
||||||
argv = uv_setup_args(argc, argv);
|
argv = uv_setup_args(argc, argv);
|
||||||
|
|
||||||
@ -1027,6 +1017,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()) {
|
||||||
|
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") {
|
||||||
|
fprintf(stderr, "Mapping to large pages is not supported.\n");
|
||||||
|
}
|
||||||
|
#endif // NODE_ENABLE_LARGE_CODE_PAGES
|
||||||
|
|
||||||
if (per_process::cli_options->print_version) {
|
if (per_process::cli_options->print_version) {
|
||||||
printf("%s\n", NODE_VERSION);
|
printf("%s\n", NODE_VERSION);
|
||||||
result.exit_code = 0;
|
result.exit_code = 0;
|
||||||
|
@ -63,6 +63,11 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) {
|
|||||||
"used, not both");
|
"used, not both");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (use_largepages != "off" &&
|
||||||
|
use_largepages != "on" &&
|
||||||
|
use_largepages != "silent") {
|
||||||
|
errors->push_back("invalid value for --use-largepages");
|
||||||
|
}
|
||||||
per_isolate->CheckOptions(errors);
|
per_isolate->CheckOptions(errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,6 +757,10 @@ PerProcessOptionsParser::PerProcessOptionsParser(
|
|||||||
kAllowedInEnvironment);
|
kAllowedInEnvironment);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
AddOption("--use-largepages",
|
||||||
|
"Map the Node.js static code to large pages",
|
||||||
|
&PerProcessOptions::use_largepages,
|
||||||
|
kAllowedInEnvironment);
|
||||||
|
|
||||||
Insert(iop, &PerProcessOptions::get_per_isolate_options);
|
Insert(iop, &PerProcessOptions::get_per_isolate_options);
|
||||||
}
|
}
|
||||||
|
@ -237,6 +237,7 @@ class PerProcessOptions : public Options {
|
|||||||
bool force_fips_crypto = false;
|
bool force_fips_crypto = false;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
std::string use_largepages = "off";
|
||||||
|
|
||||||
#ifdef NODE_REPORT
|
#ifdef NODE_REPORT
|
||||||
std::vector<std::string> cmdline;
|
std::vector<std::string> cmdline;
|
||||||
|
29
test/parallel/test-startup-large-pages.js
Normal file
29
test/parallel/test-startup-large-pages.js
Normal file
@ -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.
|
Loading…
x
Reference in New Issue
Block a user