Skip to content

Commit b4d01ba

Browse files
Gabriel Schulhofdevnexen
Gabriel Schulhof
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. PR-URL: nodejs#30954 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: James M Snell <jasnell@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>
1 parent d5b5b22 commit b4d01ba

10 files changed

+102
-58
lines changed

configure.py

-33
Original file line numberDiff line numberDiff line change
@@ -392,17 +392,6 @@
392392
dest='with_etw',
393393
help='build with ETW (default is true on Windows)')
394394

395-
parser.add_option('--use-largepages',
396-
action='store_true',
397-
dest='node_use_large_pages',
398-
help='build with Large Pages support. This feature is supported only on Linux kernel' +
399-
'>= 2.6.38 with Transparent Huge pages enabled')
400-
401-
parser.add_option('--use-largepages-script-lld',
402-
action='store_true',
403-
dest='node_use_large_pages_script_lld',
404-
help='link against the LLVM ld linker script. Implies -fuse-ld=lld in the linker flags')
405-
406395
intl_optgroup.add_option('--with-intl',
407396
action='store',
408397
dest='with_intl',
@@ -1030,28 +1019,6 @@ def configure_node(o):
10301019
else:
10311020
o['variables']['node_use_dtrace'] = 'false'
10321021

1033-
if options.node_use_large_pages and not flavor in ('linux', 'freebsd', 'mac'):
1034-
raise Exception(
1035-
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
1036-
if options.node_use_large_pages and flavor in ('linux', 'freebsd', 'mac'):
1037-
if options.shared or options.enable_static:
1038-
raise Exception(
1039-
'Large pages are supported only while creating node executable.')
1040-
if target_arch!="x64":
1041-
raise Exception(
1042-
'Large pages are supported only x64 platform.')
1043-
if flavor == 'mac':
1044-
info('macOS server with 32GB or more is recommended')
1045-
if flavor == 'linux':
1046-
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1047-
FULL_KERNEL_VERSION=os.uname()[2]
1048-
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1049-
if KERNEL_VERSION < "2.6.38" and flavor == 'linux':
1050-
raise Exception(
1051-
'Large pages need Linux kernel version >= 2.6.38')
1052-
o['variables']['node_use_large_pages'] = b(options.node_use_large_pages)
1053-
o['variables']['node_use_large_pages_script_lld'] = b(options.node_use_large_pages_script_lld)
1054-
10551022
if options.no_ifaddrs:
10561023
o['defines'] += ['SUNOS_NO_IFADDRS']
10571024

doc/api/cli.md

+17
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,22 @@ environment variables.
480480

481481
See `SSL_CERT_DIR` and `SSL_CERT_FILE`.
482482

483+
### `--use-largepages=mode`
484+
<!-- YAML
485+
added: REPLACEME
486+
-->
487+
488+
Re-map the Node.js static code to large memory pages at startup. If supported on
489+
the target system, this will cause the Node.js static code to be moved onto 2
490+
MiB pages instead of 4 KiB pages.
491+
492+
The following values are valid for `mode`:
493+
* `off`: No mapping will be attempted. This is the default.
494+
* `on`: If supported by the OS, mapping will be attempted. Failure to map will
495+
be ignored and a message will be printed to standard error.
496+
* `silent`: If supported by the OS, mapping will be attempted. Failure to map
497+
will be ignored and will not be reported.
498+
483499
### `--v8-options`
484500
<!-- YAML
485501
added: v0.1.3
@@ -676,6 +692,7 @@ Node.js options that are allowed are:
676692
- `--track-heap-objects`
677693
- `--unhandled-rejections`
678694
- `--use-bundled-ca`
695+
- `--use-largepages`
679696
- `--use-openssl-ca`
680697
- `--v8-pool-size`
681698
- `--zero-fill-buffers`

doc/node.1

+10
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,16 @@ See
259259
and
260260
.Ev SSL_CERT_FILE .
261261
.
262+
.It Fl -use-largepages Ns = Ns Ar mode
263+
Re-map the Node.js static code to large memory pages at startup. If supported on
264+
the target system, this will cause the Node.js static code to be moved onto 2
265+
MiB pages instead of 4 KiB pages.
266+
.Pp
267+
.Ar mode
268+
must have one of the following values:
269+
`off` (the default value, meaning do not map), `on` (map and ignore failure,
270+
reporting it to stderr), or `silent` (map and silently ignore failure).
271+
.
262272
.It Fl -v8-options
263273
Print V8 command-line options.
264274
.

node.gyp

+2-3
Original file line numberDiff line numberDiff line change
@@ -606,10 +606,9 @@
606606
'src/tls_wrap.h'
607607
],
608608
}],
609-
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"', {
609+
[ 'OS in "linux freebsd mac" and '
610+
'target_arch=="x64"', {
610611
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
611-
# The current implementation of Large Pages is under Linux.
612-
# Other implementations are possible but not currently supported.
613612
'sources': [
614613
'src/large_pages/node_large_page.cc',
615614
'src/large_pages/node_large_page.h'

node.gypi

+2-4
Original file line numberDiff line numberDiff line change
@@ -306,17 +306,15 @@
306306
}],
307307
[ 'OS=="linux" and '
308308
'target_arch=="x64" and '
309-
'node_use_large_pages=="true" and '
310-
'node_use_large_pages_script_lld=="false"', {
309+
'llvm_version==0', {
311310
'ldflags': [
312311
'-Wl,-T',
313312
'<!(realpath src/large_pages/ld.implicit.script)',
314313
]
315314
}],
316315
[ 'OS=="linux" and '
317316
'target_arch=="x64" and '
318-
'node_use_large_pages=="true" and '
319-
'node_use_large_pages_script_lld=="true"', {
317+
'llvm_version!=0', {
320318
'ldflags': [
321319
'-Wl,-T',
322320
'<!(realpath src/large_pages/ld.implicit.script.lld)',

src/large_pages/node_large_page.cc

+12-8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
//
2121
// SPDX-License-Identifier: MIT
2222

23+
#include "node_internals.h"
2324
#include "node_large_page.h"
2425
#include "util.h"
2526
#include "uv.h"
@@ -69,7 +70,7 @@
6970
// Map a new area and copy the original code there
7071
// Use mmap using the start address with MAP_FIXED so we get exactly the
7172
// same virtual address
72-
// Use madvise with MADV_HUGE_PAGE to use Anonymous 2M Pages
73+
// Use madvise with MADV_HUGEPAGE to use Anonymous 2M Pages
7374
// If successful copy the code there and unmap the original region.
7475

7576
extern char __nodetext;
@@ -315,7 +316,7 @@ static bool IsSuperPagesEnabled() {
315316
// a. map a new area and copy the original code there
316317
// b. mmap using the start address with MAP_FIXED so we get exactly
317318
// the same virtual address (except on macOS).
318-
// c. madvise with MADV_HUGE_PAGE
319+
// c. madvise with MADV_HUGEPAGE
319320
// d. If successful copy the code there and unmap the original region
320321
int
321322
#if !defined(__APPLE__)
@@ -340,9 +341,6 @@ MoveTextRegionToLargePages(const text_region& r) {
340341
PrintSystemError(errno);
341342
return -1;
342343
}
343-
OnScopeLeave munmap_on_return([nmem, size]() {
344-
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
345-
});
346344

347345
memcpy(nmem, r.from, size);
348346

@@ -359,13 +357,14 @@ MoveTextRegionToLargePages(const text_region& r) {
359357
return -1;
360358
}
361359

362-
ret = madvise(tmem, size, MADV_HUGEPAGE);
360+
ret = madvise(tmem, size, 14 /* MADV_HUGEPAGE */);
363361
if (ret == -1) {
364362
PrintSystemError(errno);
365363
ret = munmap(tmem, size);
366364
if (ret == -1) {
367365
PrintSystemError(errno);
368366
}
367+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
369368
return -1;
370369
}
371370
memcpy(start, nmem, size);
@@ -376,6 +375,7 @@ MoveTextRegionToLargePages(const text_region& r) {
376375
MAP_ALIGNED_SUPER, -1 , 0);
377376
if (tmem == MAP_FAILED) {
378377
PrintSystemError(errno);
378+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
379379
return -1;
380380
}
381381
#elif defined(__APPLE__)
@@ -390,6 +390,7 @@ MoveTextRegionToLargePages(const text_region& r) {
390390
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
391391
if (tmem == MAP_FAILED) {
392392
PrintSystemError(errno);
393+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
393394
return -1;
394395
}
395396
memcpy(tmem, nmem, size);
@@ -400,6 +401,7 @@ MoveTextRegionToLargePages(const text_region& r) {
400401
if (ret == -1) {
401402
PrintSystemError(errno);
402403
}
404+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
403405
return -1;
404406
}
405407
memcpy(start, tmem, size);
@@ -412,8 +414,10 @@ MoveTextRegionToLargePages(const text_region& r) {
412414
if (ret == -1) {
413415
PrintSystemError(errno);
414416
}
417+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
415418
return -1;
416419
}
420+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
417421
return ret;
418422
}
419423

@@ -425,12 +429,12 @@ int MapStaticCodeToLargePages() {
425429
return -1;
426430
}
427431

428-
#if defined(__linux__)
432+
#if defined(__linux__) || defined(__FreeBSD__)
429433
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
430434
return MoveTextRegionToLargePages(r);
431435

432436
return -1;
433-
#elif defined(__FreeBSD__) || defined(__APPLE__)
437+
#elif defined(__APPLE__)
434438
return MoveTextRegionToLargePages(r);
435439
#endif
436440
}

src/node.cc

+20-10
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,7 @@
7474
#include "../deps/v8/src/third_party/vtune/v8-vtune.h"
7575
#endif
7676

77-
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
7877
#include "large_pages/node_large_page.h"
79-
#endif
8078

8179
#include <errno.h>
8280
#include <fcntl.h> // _O_RDWR
@@ -3021,14 +3019,6 @@ int Start(int argc, char** argv) {
30213019

30223020
CHECK_GT(argc, 0);
30233021

3024-
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
3025-
if (node::IsLargePagesEnabled()) {
3026-
if (node::MapStaticCodeToLargePages() != 0) {
3027-
fprintf(stderr, "Reverting to default page size\n");
3028-
}
3029-
}
3030-
#endif
3031-
30323022
// Hack around with the argv pointer. Used for process.title = "blah".
30333023
argv = uv_setup_args(argc, argv);
30343024

@@ -3037,6 +3027,26 @@ int Start(int argc, char** argv) {
30373027
// This needs to run *before* V8::Initialize().
30383028
Init(&args, &exec_args);
30393029

3030+
#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
3031+
if (per_process_opts->use_largepages == "on" ||
3032+
per_process_opts->use_largepages == "silent") {
3033+
if (node::IsLargePagesEnabled()) {
3034+
if (node::MapStaticCodeToLargePages() != 0 &&
3035+
per_process_opts->use_largepages != "silent") {
3036+
fprintf(stderr,
3037+
"Mapping code to large pages failed. Reverting to default page "
3038+
"size.\n");
3039+
}
3040+
} else if (per_process_opts->use_largepages != "silent") {
3041+
fprintf(stderr, "Large pages are not enabled.\n");
3042+
}
3043+
}
3044+
#else
3045+
if (per_process_opts->use_largepages == "on") {
3046+
fprintf(stderr, "Mapping to large pages is not supported.\n");
3047+
}
3048+
#endif // NODE_ENABLE_LARGE_CODE_PAGES
3049+
30403050
#if HAVE_OPENSSL
30413051
{
30423052
std::string extra_ca_certs;

src/node_options.cc

+9
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors) {
2424
"used, not both");
2525
}
2626
#endif
27+
if (use_largepages != "off" &&
28+
use_largepages != "on" &&
29+
use_largepages != "silent") {
30+
errors->push_back("invalid value for --use-largepages");
31+
}
2732
per_isolate->CheckOptions(errors);
2833
}
2934

@@ -355,6 +360,10 @@ PerProcessOptionsParser::PerProcessOptionsParser() {
355360
kAllowedInEnvironment);
356361
#endif
357362
#endif
363+
AddOption("--use-largepages",
364+
"Map the Node.js static code to large pages",
365+
&PerProcessOptions::use_largepages,
366+
kAllowedInEnvironment);
358367

359368
Insert(&PerIsolateOptionsParser::instance,
360369
&PerProcessOptions::get_per_isolate_options);

src/node_options.h

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class PerProcessOptions : public Options {
153153
bool force_fips_crypto = false;
154154
#endif
155155
#endif
156+
std::string use_largepages = "off";
156157

157158
inline PerIsolateOptions* get_per_isolate_options();
158159
void CheckOptions(std::vector<std::string>* errors);
+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)