Skip to content

Commit 2ffab9f

Browse files
sawenzelpcanal
authored andcommitted
Option to reduce startup syscalls via environment caching
This commit provides the possibility to pass system library search paths as well as some compiler include paths to ROOT as environment variables. This has the advantage that ROOT will spawn less sub-processes and we can do the setup only once, instead of doing it for every single executable that is linked to ROOT. The commit does not change any default behaviour! Rather, expert-users may use the new feature by moving the initialization of the search paths to say software environment loading. In ALICE, we do something like ``` export ROOT_LDSYSPATH=$(LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls /tmp/DOESNOTEXIST 2>&1 | grep -m 1 "system search path" | sed 's/.*=//g' | awk '//{print $1}') export CLING_LDSYSPATH=ROOT_LDSYSPATH export CLING_CPPSYSINCL=$(LC_ALL=C c++ -xc++ -E -v /dev/null 2>&1 | sed -n '/^.include/,${/^ \/.*++/{p}}' | tr '\n' ':' | tr ' ' ':') ``` speeding up the initialization of our executables at runtime and doing less syscalls that create short-lived processes, for instance calling the compiler. The effect from this operation can be seen by counting the `execve` syscalls in a small example: ``` strace -e execve -f root.exe -q -e "double x=1;" # ---> 14 calls export ROOT_LDSYSPATH=... export CLING_CPPSYSINCL=... strace -e execve -f root.exe -q -e "double x=1;" # ---> 6 calls ``` This gain can accumulate to significant savings when used in a multi-process environment such as ALICE is using.
1 parent d3767a4 commit 2ffab9f

File tree

4 files changed

+73
-18
lines changed

4 files changed

+73
-18
lines changed

README/ReleaseNotes/v638/index.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ The following people have contributed to this new version:
3131
Silia Taider, CERN/EP-SFT,\
3232
Florian Uhlig, GSI,\
3333
Devajith Valaparambil Sreeramaswamy, CERN/EP-SFT,\
34-
Vassil Vassilev, Princeton
34+
Vassil Vassilev, Princeton,\
35+
Sandro Wenzel, CERN/ALICE
3536

3637
## Deprecation and Removal
3738

@@ -49,6 +50,13 @@ The following people have contributed to this new version:
4950

5051
## Core Libraries
5152
* Behavior change: when selecting a template instantiation for a dictionary, all the template arguments have to be fully defined - the forward declarations are not enough any more. The error prompted by the dictionary generator will be `Warning: Unused class rule: MyTemplate<MyFwdDeclaredClass>`.
53+
* New expert option to reduce static startup cost of ROOT by setting environment variables
54+
```bash
55+
export ROOT_LDSYSPATH=$(LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls /tmp/DOESNOTEXIST 2>&1 | grep -m 1 "system search path" | sed 's/.*=//g' | awk '//{print $1}')
56+
export CLING_LDSYSPATH=ROOT_LDSYSPATH
57+
export CLING_CPPSYSINCL=$(LC_ALL=C c++ -xc++ -E -v /dev/null 2>&1 | sed -n '/^.include/,${/^ \/.*++/{p}}' | tr '\n' ':' | tr ' ' ':')
58+
```
59+
This caching reduces sub-process creation during initialization and can be useful when multiple ROOT instances or binaries linked to ROOT are executed (less system-calls, cleaner debugging).
5260

5361
## I/O
5462

core/unix/src/TUnixSystem.cxx

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4599,6 +4599,12 @@ int TUnixSystem::UnixSend(int sock, const void *buffer, int length, int flag)
45994599

46004600
////////////////////////////////////////////////////////////////////////////////
46014601
/// Get shared library search path. Static utility function.
4602+
///
4603+
/// The runtime cost (syscalls) of this function can be shortened by injecting
4604+
/// a pre-calculated result for the system library search path in form of
4605+
/// `export ROOT_LDSYSPATH=$(LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls /tmp/DOESNOTEXIST 2>&1 | grep -m 1 "system search
4606+
/// path" | sed 's/.*=//g' | awk '//{print $1}')` This might be useful in scenarios, where ROOT is instantiated many
4607+
/// times.
46024608

46034609
static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFALSE)
46044610
{
@@ -4684,23 +4690,34 @@ static const char *DynamicPath(const char *newpath = nullptr, Bool_t reset = kFA
46844690
dynpath_syspart = "/usr/local/lib:/usr/X11R6/lib:/usr/lib:/lib:";
46854691
dynpath_syspart += "/lib/x86_64-linux-gnu:/usr/local/lib64:/usr/lib64:/lib64:";
46864692
#else
4687-
// trick to get the system search path
4688-
std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
4689-
FILE *pf = popen(cmd.c_str (), "r");
4690-
std::string result = "";
4691-
char buffer[128];
4692-
while (!feof(pf)) {
4693-
if (fgets(buffer, 128, pf) != nullptr)
4694-
result += buffer;
4695-
}
4696-
pclose(pf);
4697-
std::size_t from = result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
4698-
std::size_t to = result.find("(system search path)");
4699-
if (from != std::string::npos && to != std::string::npos) {
4700-
from += 12;
4701-
std::string sys_path = result.substr(from, to-from);
4702-
sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace), sys_path.end());
4703-
dynpath_syspart = sys_path.c_str();
4693+
// Obtain the system search path ...
4694+
4695+
// First of all, let's see if an outside entity gave us the system path.
4696+
// This is a power-user feature for users bringing up many executables with ROOT linked in.
4697+
// In this case, the system path could be cached in an environment variable ROOT_LDSYSPATH and prevent
4698+
// repeated sys-calls (popen) and to having to search through potentially long LD_LIBRARY_PATH strings.
4699+
const auto ldsyspath = std::getenv("ROOT_LDSYSPATH");
4700+
if (ldsyspath != nullptr) {
4701+
dynpath_syspart = ldsyspath;
4702+
} else {
4703+
// trick to get the system search path at runtime (default case)
4704+
std::string cmd("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls 2>&1");
4705+
FILE *pf = popen(cmd.c_str(), "r");
4706+
std::string result = "";
4707+
char buffer[128];
4708+
while (!feof(pf)) {
4709+
if (fgets(buffer, 128, pf) != nullptr)
4710+
result += buffer;
4711+
}
4712+
pclose(pf);
4713+
std::size_t from = result.find("search path=", result.find("(LD_LIBRARY_PATH)"));
4714+
std::size_t to = result.find("(system search path)");
4715+
if (from != std::string::npos && to != std::string::npos) {
4716+
from += 12;
4717+
std::string sys_path = result.substr(from, to - from);
4718+
sys_path.erase(std::remove_if(sys_path.begin(), sys_path.end(), isspace), sys_path.end());
4719+
dynpath_syspart = sys_path.c_str();
4720+
}
47044721
}
47054722
dynpath_envpart.ReplaceAll("::", ":");
47064723
dynpath_syspart.ReplaceAll("::", ":");

interpreter/cling/lib/Interpreter/CIFactory.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,23 @@ namespace {
114114
llvm::SmallVectorImpl<char>& Buf,
115115
AdditionalArgList& Args,
116116
bool Verbose) {
117+
// For power-users: Let's see if the path is available as a predefined env
118+
// variable to save repeated system calls when ROOT/CLING is initialized for
119+
// each process in a many process system. CLING_CPPSYSINCL should contain
120+
// paths separated by a ":" and otherwise contain the same paths as returned
121+
// from the CppInclQuery further below.
122+
auto PrefCppSystemIncl = std::getenv("CLING_CPPSYSINCL");
123+
if (PrefCppSystemIncl != nullptr) {
124+
llvm::StringRef PathsString(PrefCppSystemIncl);
125+
llvm::SmallVector<StringRef, 10> Paths;
126+
PathsString.split(Paths, ":");
127+
for (auto& P : Paths) {
128+
P = P.trim();
129+
Args.addArgument("-cxx-isystem", P.str());
130+
}
131+
return;
132+
}
133+
117134
std::string CppInclQuery("LC_ALL=C ");
118135
CppInclQuery.append(Compiler);
119136

interpreter/cling/lib/Utils/PlatformPosix.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,19 @@ bool GetSystemLibraryPaths(llvm::SmallVectorImpl<std::string>& Paths) {
182182
Paths.push_back("/lib64/");
183183
#endif
184184
#else
185+
// Power-user mode: See if the result of this query is cached/provided in env
186+
// variable to avoid sys-calls and spawning processes
187+
auto ldsyspath = std::getenv("CLING_LDSYSPATH");
188+
if (ldsyspath != nullptr) {
189+
std::string SysPath(ldsyspath);
190+
llvm::SmallVector<llvm::StringRef, 10> CurPaths;
191+
SplitPaths(SysPath, CurPaths);
192+
for (const auto& Path : CurPaths) {
193+
Paths.push_back(Path.str());
194+
}
195+
return true;
196+
}
197+
185198
llvm::SmallString<1024> Buf;
186199
platform::Popen("LD_DEBUG=libs LD_PRELOAD=DOESNOTEXIST ls", Buf, true);
187200
const llvm::StringRef Result = Buf.str();

0 commit comments

Comments
 (0)