-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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: use _init as the start of large pages #31947
src: use _init as the start of large pages #31947
Conversation
@suresh-srinivas @uttampawar PTAL! |
clang++ seems pretty averse to emitting
That's interesting, do you know why that is? The current parser can probably be made to accommodate that without switching to a different symbol. |
@bnoordhuis I tried keeping |
@bnoordhuis just looking at |
@bnoordhuis got any particular combination of Linux version/clang version where clang doesn't produce |
I'm testing with clang 9 but even after grepping through the llvm+clang+lld source tree I haven't really narrowed down what causes clang to emit them or not. 🤷♂ At any rate, does it matter where |
Could you remind me why we are not using the linker script? The default linker script with @gabrielschulhof werent you trying an alternate way to use an assembly file to provide this as part of the first node build file?
|
Yes it matters where |
They're hard to keep portable across ld.bfd, ld.gold and ld.ldd.
We scan /proc/self/maps for a mapping that contains a symbol. I don't see why it matters if that symbol is at the start, the end or somewhere in the middle of that segment; it's copied whole. |
I thought node is build only with gcc and ld. Are all these supported as valid linkers?
The symbol is used to determine the start address of the region that we are copying from. The original code with the ld script was using it like this.
|
@suresh-srinivas I was, based on https://bugzilla.redhat.com/show_bug.cgi?id=927573, but it's really difficult to get the object file to be the very first file passed to the linker, and even then I was unable to get the symbol to go all the way to the beginning of the .text section. |
Actually, we cannot copy the whole thing. For example, on Skylake there is a significant distance between The mapping may include multiple executable sections. We only want to relocate the .text section. |
@bnoordhuis another way of looking at this is that it's not so much a question of whether or not clang generates I also checked and it is distro-related rather than processor-related. I ran Ubuntu 18.04 on my laptop in a VM and it also produces the same kind of mapping as on the Skylake machine I ran it on earlier. |
6e66ea5
to
b2a0512
Compare
@bnoordhuis @devnexen @lundibundi I changed the implementation to not use |
The assumption that the .text section starts at the mapping following the one that contains `__executable_start` is not valid on 64-bit Ubuntu 18.04 where the whole code resides in one mapping. OTOH, The symbol `_init` is usually located at the beginning of the .text section and it does not need the assumption that the next mapping has the .text section. Thus, we use the symbol, if available, to perform the mapping on Ubuntu 18.04. We also rename the section into which we place the remapping code to `lpstub`. This causes the linker to produce symbols `__start_lpstub` and `__stop_lpstub`, the latter of which we do not use. Still, `__start_lpstub` helps us find the end of the .text section because on Ubuntu 18.04 this section is inserted before the end of the sole mapping, so we use `__start_lpstub` as the end instead of the end of the mapping.
b2a0512
to
11f675c
Compare
You have to walk me through this. Ultimately, at least on Linux, the goal is to call diff --git a/src/node_main.cc b/src/node_main.cc
index e92c0df942..1a163ec2a6 100644
--- a/src/node_main.cc
+++ b/src/node_main.cc
@@ -75,6 +75,7 @@ int wmain(int argc, wchar_t* wargv[]) {
// UNIX
#ifdef __linux__
#include <elf.h>
+#include <sys/mman.h>
#ifdef __LP64__
#define Elf_auxv_t Elf64_auxv_t
#else
@@ -94,6 +95,12 @@ extern bool linux_at_secure;
} // namespace node
int main(int argc, char* argv[]) {
+#if defined(__linux__)
+ constexpr uintptr_t k2MB = 1 << 21;
+ void* const addr =
+ reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&main) & -k2MB);
+ if (-1 == madvise(addr, k2MB, MADV_HUGEPAGE)) perror("madvise");
+#endif // __linux__
#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
// In node::PlatformInit(), we squash all signal handlers for non-shared lib
// build. In order to run test cases against shared lib build, we also need Apart from the lack of sophistication, isn't that sufficient to turn on THP? |
@bnoordhuis currently the Node.js .text section takes up about 14-20 MiB, depending on whether you're using clang or gcc. The order in which the functions appear in the .text section is uncertain. We would like to capture as much of the .text section as possible, while leaving out the section containing the code that does the remapping itself. If we do not capture as much of .text as possible, we risk leaving the hot parts of the code mapped onto 4KiB pages, thereby forfeiting the benefits of re-mapping to large pages. I believe, and @suresh-srinivas can give more detail, that we cannot simply At any rate, please also have a look at #31981 which is an alternative implementation. Basically, instead of a linker script, it uses an object file compiled from an assembly file. The assembly introduces a symbol into the text section, much as the linker script did before. Since we cannot use a linker script to ensure that the symbol is placed at the beginning of the .text section, we must ensure that the object file resulting from the assembly file is the first item to be linked on the linker's command line. Assuming the linker places the code more or less in the order it encounters the object/archive files ones gives it, the symbol should end up at or near the beginning of the .text section and still give us a good chunk of it for remapping. |
Closing in favour of #31981. |
The assumption that the .text section starts at the mapping following
the one that contains
__executable_start
is not valid on Skylake CPUswhere the whole code resides in one mapping.
OTOH, The symbol
_init
is usually located at the beginning of the.text section and it does not need the assumption that the next
mapping has the .text section.
We also rename the section into which we place the remapping code to
lpstub
. This causes the linker to produce symbols__start_lpstub
and
__stop_lpstub
, the latter of which we do not use. Still,__start_lpstub
helps us find the end of the .text section because onSkylake this section is inserted before the end of the mapping, so we
use
__start_lpstub
as the end instead of the end of the mapping.Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes