Skip to content

Commit 8486399

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: #31063 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 2445bc0 commit 8486399

File tree

10 files changed

+101
-58
lines changed

10 files changed

+101
-58
lines changed

‎configure.py‎

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -398,17 +398,6 @@
398398
dest='with_etw',
399399
help='build with ETW (default is true on Windows)')
400400

401-
parser.add_option('--use-largepages',
402-
action='store_true',
403-
dest='node_use_large_pages',
404-
help='build with Large Pages support. This feature is supported only on Linux kernel'+
405-
'>= 2.6.38 with Transparent Huge pages enabled and FreeBSD')
406-
407-
parser.add_option('--use-largepages-script-lld',
408-
action='store_true',
409-
dest='node_use_large_pages_script_lld',
410-
help='link against the LLVM ld linker script. Implies -fuse-ld=lld in the linker flags')
411-
412401
intl_optgroup.add_option('--with-intl',
413402
action='store',
414403
dest='with_intl',
@@ -1041,28 +1030,6 @@ def configure_node(o):
10411030
else:
10421031
o['variables']['node_use_dtrace'] ='false'
10431032

1044-
ifoptions.node_use_large_pagesandnotflavorin ('linux', 'freebsd', 'mac'):
1045-
raiseException(
1046-
'Large pages are supported only on Linux, FreeBSD and MacOS Systems.')
1047-
ifoptions.node_use_large_pagesandflavorin ('linux', 'freebsd', 'mac'):
1048-
ifoptions.sharedoroptions.enable_static:
1049-
raiseException(
1050-
'Large pages are supported only while creating node executable.')
1051-
iftarget_arch!="x64":
1052-
raiseException(
1053-
'Large pages are supported only x64 platform.')
1054-
ifflavor=='mac':
1055-
info('macOS server with 32GB or more is recommended')
1056-
ifflavor=='linux':
1057-
# Example full version string: 2.6.32-696.28.1.el6.x86_64
1058-
FULL_KERNEL_VERSION=os.uname()[2]
1059-
KERNEL_VERSION=FULL_KERNEL_VERSION.split('-')[0]
1060-
ifKERNEL_VERSION<"2.6.38"andflavor=='linux':
1061-
raiseException(
1062-
'Large pages need Linux kernel version >= 2.6.38')
1063-
o['variables']['node_use_large_pages'] =b(options.node_use_large_pages)
1064-
o['variables']['node_use_large_pages_script_lld'] =b(options.node_use_large_pages_script_lld)
1065-
10661033
ifoptions.no_ifaddrs:
10671034
o['defines'] += ['SUNOS_NO_IFADDRS']
10681035

‎doc/api/cli.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,6 +843,22 @@ environment variables.
843843

844844
See `SSL_CERT_DIR` and `SSL_CERT_FILE`.
845845

846+
### `--use-largepages=mode`
847+
<!-- YAML
848+
added: REPLACEME
849+
-->
850+
851+
Re-map the Node.js static code to large memory pages at startup. If supported on
852+
the target system, this will cause the Node.js static code to be moved onto 2
853+
MiB pages instead of 4 KiB pages.
854+
855+
The following values are valid for `mode`:
856+
*`off`: No mapping will be attempted. This is the default.
857+
*`on`: If supported by the OS, mapping will be attempted. Failure to map will
858+
be ignored and a message will be printed to standard error.
859+
*`silent`: If supported by the OS, mapping will be attempted. Failure to map
860+
will be ignored and will not be reported.
861+
846862
### `--v8-options`
847863
<!-- YAML
848864
added: v0.1.3
@@ -1096,6 +1112,7 @@ Node.js options that are allowed are:
10961112
*`--track-heap-objects`
10971113
*`--unhandled-rejections`
10981114
*`--use-bundled-ca`
1115+
*`--use-largepages`
10991116
*`--use-openssl-ca`
11001117
*`--v8-pool-size`
11011118
*`--zero-fill-buffers`

‎doc/node.1‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,16 @@ See
377377
and
378378
.EvSSL_CERT_FILE .
379379
.
380+
.ItFl-use-largepagesNs=NsArmode
381+
Re-map the Node.js static code to large memory pages at startup. If supported on
382+
the target system, this will cause the Node.js static code to be moved onto 2
383+
MiB pages instead of 4 KiB pages.
384+
.Pp
385+
.Armode
386+
must have one of the following values:
387+
`off` (the default value, meaning do not map), `on` (map and ignore failure,
388+
reporting it to stderr), or `silent` (map and silently ignore failure).
389+
.
380390
.ItFl-v8-options
381391
Print V8 command-line options.
382392
.

‎node.gyp‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -824,10 +824,9 @@
824824
}],
825825
],
826826
}],
827-
[ 'node_use_large_pages=="true" and OS in "linux freebsd mac"',{
827+
[ 'OS in "linux freebsd mac" and '
828+
'target_arch=="x64"',{
828829
'defines': [ 'NODE_ENABLE_LARGE_CODE_PAGES=1' ],
829-
# The current implementation of Large Pages is under Linux.
830-
# Other implementations are possible but not currently supported.
831830
'sources': [
832831
'src/large_pages/node_large_page.cc',
833832
'src/large_pages/node_large_page.h'

‎node.gypi‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -302,17 +302,15 @@
302302
}],
303303
[ 'OS=="linux" and '
304304
'target_arch=="x64" and '
305-
'node_use_large_pages=="true" and '
306-
'node_use_large_pages_script_lld=="false"',{
305+
'llvm_version=="0.0"',{
307306
'ldflags': [
308307
'-Wl,-T',
309308
'<!(realpath src/large_pages/ld.implicit.script)',
310309
]
311310
}],
312311
[ 'OS=="linux" and '
313312
'target_arch=="x64" and '
314-
'node_use_large_pages=="true" and '
315-
'node_use_large_pages_script_lld=="true"',{
313+
'llvm_version!="0.0"',{
316314
'ldflags': [
317315
'-Wl,-T',
318316
'<!(realpath src/large_pages/ld.implicit.script.lld)',

‎src/large_pages/node_large_page.cc‎

Lines changed: 11 additions & 8 deletions
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
externchar __nodetext;
@@ -308,7 +308,7 @@ static bool IsSuperPagesEnabled(){
308308
// a. map a new area and copy the original code there
309309
// b. mmap using the start address with MAP_FIXED so we get exactly
310310
// the same virtual address (except on macOS).
311-
// c. madvise with MADV_HUGE_PAGE
311+
// c. madvise with MADV_HUGEPAGE
312312
// d. If successful copy the code there and unmap the original region
313313
int
314314
#if !defined(__APPLE__)
@@ -333,9 +333,6 @@ MoveTextRegionToLargePages(const text_region& r){
333333
PrintSystemError(errno);
334334
return -1;
335335
}
336-
OnScopeLeave munmap_on_return([nmem, size](){
337-
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
338-
});
339336

340337
memcpy(nmem, r.from, size);
341338

@@ -352,13 +349,14 @@ MoveTextRegionToLargePages(const text_region& r){
352349
return -1;
353350
}
354351

355-
ret = madvise(tmem, size, MADV_HUGEPAGE);
352+
ret = madvise(tmem, size, 14/*MADV_HUGEPAGE*/);
356353
if (ret == -1){
357354
PrintSystemError(errno);
358355
ret = munmap(tmem, size);
359356
if (ret == -1){
360357
PrintSystemError(errno);
361358
}
359+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
362360
return -1;
363361
}
364362
memcpy(start, nmem, size);
@@ -369,6 +367,7 @@ MoveTextRegionToLargePages(const text_region& r){
369367
MAP_ALIGNED_SUPER, -1 , 0);
370368
if (tmem == MAP_FAILED){
371369
PrintSystemError(errno);
370+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
372371
return -1;
373372
}
374373
#elif defined(__APPLE__)
@@ -383,6 +382,7 @@ MoveTextRegionToLargePages(const text_region& r){
383382
VM_FLAGS_SUPERPAGE_SIZE_2MB, 0);
384383
if (tmem == MAP_FAILED){
385384
PrintSystemError(errno);
385+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
386386
return -1;
387387
}
388388
memcpy(tmem, nmem, size);
@@ -393,6 +393,7 @@ MoveTextRegionToLargePages(const text_region& r){
393393
if (ret == -1){
394394
PrintSystemError(errno);
395395
}
396+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
396397
return -1;
397398
}
398399
memcpy(start, tmem, size);
@@ -405,8 +406,10 @@ MoveTextRegionToLargePages(const text_region& r){
405406
if (ret == -1){
406407
PrintSystemError(errno);
407408
}
409+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
408410
return -1;
409411
}
412+
if (-1 == munmap(nmem, size)) PrintSystemError(errno);
410413
return ret;
411414
}
412415

@@ -418,12 +421,12 @@ int MapStaticCodeToLargePages(){
418421
return -1;
419422
}
420423

421-
#if defined(__linux__)
424+
#if defined(__linux__) || defined(__FreeBSD__)
422425
if (r.from > reinterpret_cast<void*>(&MoveTextRegionToLargePages))
423426
returnMoveTextRegionToLargePages(r);
424427

425428
return -1;
426-
#elif defined(__FreeBSD__) || defined(__APPLE__)
429+
#elif defined(__APPLE__)
427430
returnMoveTextRegionToLargePages(r);
428431
#endif
429432
}

‎src/node.cc‎

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@
6464
#include"inspector/worker_inspector.h"// ParentInspectorHandle
6565
#endif
6666

67-
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
6867
#include"large_pages/node_large_page.h"
69-
#endif
7068

7169
#ifdef NODE_REPORT
7270
#include"node_report.h"
@@ -887,14 +885,6 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv){
887885

888886
CHECK_GT(argc, 0);
889887

890-
#ifdef NODE_ENABLE_LARGE_CODE_PAGES
891-
if (node::IsLargePagesEnabled()){
892-
if (node::MapStaticCodeToLargePages() != 0){
893-
fprintf(stderr, "Reverting to default page size\n");
894-
}
895-
}
896-
#endif
897-
898888
// Hack around with the argv pointer. Used for process.title = "blah".
899889
argv = uv_setup_args(argc, argv);
900890

@@ -914,6 +904,26 @@ InitializationResult InitializeOncePerProcess(int argc, char** argv){
914904
}
915905
}
916906

907+
#if defined(NODE_ENABLE_LARGE_CODE_PAGES) && NODE_ENABLE_LARGE_CODE_PAGES
908+
if (per_process::cli_options->use_largepages == "on" ||
909+
per_process::cli_options->use_largepages == "silent"){
910+
if (node::IsLargePagesEnabled()){
911+
if (node::MapStaticCodeToLargePages() != 0 &&
912+
per_process::cli_options->use_largepages != "silent"){
913+
fprintf(stderr,
914+
"Mapping code to large pages failed. Reverting to default page "
915+
"size.\n");
916+
}
917+
} elseif (per_process::cli_options->use_largepages != "silent"){
918+
fprintf(stderr, "Large pages are not enabled.\n");
919+
}
920+
}
921+
#else
922+
if (per_process::cli_options->use_largepages == "on"){
923+
fprintf(stderr, "Mapping to large pages is not supported.\n");
924+
}
925+
#endif// NODE_ENABLE_LARGE_CODE_PAGES
926+
917927
if (per_process::cli_options->print_version){
918928
printf("%s\n", NODE_VERSION);
919929
result.exit_code = 0;

‎src/node_options.cc‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ void PerProcessOptions::CheckOptions(std::vector<std::string>* errors){
6363
"used, not both");
6464
}
6565
#endif
66+
if (use_largepages != "off" &&
67+
use_largepages != "on" &&
68+
use_largepages != "silent"){
69+
errors->push_back("invalid value for --use-largepages");
70+
}
6671
per_isolate->CheckOptions(errors);
6772
}
6873

@@ -749,6 +754,10 @@ PerProcessOptionsParser::PerProcessOptionsParser(
749754
kAllowedInEnvironment);
750755
#endif
751756
#endif
757+
AddOption("--use-largepages",
758+
"Map the Node.js static code to large pages",
759+
&PerProcessOptions::use_largepages,
760+
kAllowedInEnvironment);
752761

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

‎src/node_options.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ class PerProcessOptions : public Options{
233233
bool force_fips_crypto = false;
234234
#endif
235235
#endif
236+
std::string use_largepages = "off";
236237

237238
#ifdef NODE_REPORT
238239
std::vector<std::string> cmdline;
Lines changed: 29 additions & 0 deletions
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+
constassert=require('assert');
7+
const{ spawnSync }=require('child_process');
8+
9+
{
10+
constchild=spawnSync(process.execPath,
11+
['--use-largepages=on','-p','42']);
12+
conststdout=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+
constchild=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)