gc, pal: Avoid linking libnuma.so on non-NUMA systems (#1468)
authorLeandro A. F. Pereira <leandro.pereira@microsoft.com>
Sat, 11 Jan 2020 03:34:06 +0000 (19:34 -0800)
committerStephen Toub <stoub@microsoft.com>
Sat, 11 Jan 2020 03:34:06 +0000 (22:34 -0500)
* gc, pal: Avoid linking libnuma.so on non-NUMA systems

In an effort to reduce the system calls performed during the
initialization, this seemed like another low-hanging fruit: only link
in libnuma.so if the system has more than one NUMA node.

* gc, pal: Remove unneeded dlsym() call

The result of dlsym() is never attributed to any variable, and this
function is never called, so it's safe to remove these dlsym() calls.

src/coreclr/src/gc/unix/gcenv.unix.cpp
src/coreclr/src/pal/src/numa/numa.cpp

index 6bb0b94626bf1d56482b749880324957a78e8e49..1571bf65de83c05c27e6cbb762cc22493e601647 100644 (file)
@@ -105,6 +105,9 @@ typedef cpuset_t cpu_set_t;
 #include <numa.h>
 #include <numaif.h>
 #include <dlfcn.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 // List of all functions from the numa library that are used
 #define FOR_ALL_NUMA_FUNCTIONS \
@@ -192,8 +195,49 @@ void* g_numaHandle = nullptr;
 #define PER_FUNCTION_BLOCK(fn) decltype(fn)* fn##_ptr;
 FOR_ALL_NUMA_FUNCTIONS
 #undef PER_FUNCTION_BLOCK
-#endif // HAVE_NUMA_H
 
+#if defined(__linux__)
+static bool ShouldOpenLibNuma()
+{
+    // This is a simple heuristic to determine if libnuma.so should be opened.  There's
+    // no point in linking and resolving everything in this library if we're running on
+    // a system that's not NUMA-capable.
+    int fd = open("/sys/devices/system/node/possible", O_RDONLY | O_CLOEXEC);
+
+    if (fd == -1)
+    {
+        // sysfs might not be mounted, not available, or the interface might have
+        // changed.  Return `true' here so NUMASupportInitialize() can try initializing
+        // NUMA support with libnuma.
+        return true;
+    }
+
+    while (true)
+    {
+        char buffer[32];
+        ssize_t bytesRead = read(fd, buffer, 32);
+
+        if (bytesRead == -1 && errno == EINTR)
+        {
+            continue;
+        }
+
+        close(fd);
+
+        // If an unknown error happened (bytesRead < 0), or the file was empty
+        // (bytesRead = 0), let libnuma handle this.  Otherwise, if there's just
+        // one NUMA node, don't bother linking in libnuma.
+        return (bytesRead <= 0) ? true : strncmp(buffer, "0\n", bytesRead) != 0;
+    }
+}
+#else
+static bool ShouldOpenLibNuma()
+{
+    return true;
+}
+#endif // __linux__
+
+#endif // HAVE_NUMA_H
 
 // Initialize data structures for getting and setting thread affinities to processors and
 // querying NUMA related processor information.
@@ -202,6 +246,13 @@ FOR_ALL_NUMA_FUNCTIONS
 void NUMASupportInitialize()
 {
 #if HAVE_NUMA_H
+    if (!ShouldOpenLibNuma())
+    {
+        g_numaAvailable = false;
+        g_highestNumaNode = 0;
+        return;
+    }
+
     g_numaHandle = dlopen("libnuma.so", RTLD_LAZY);
     if (g_numaHandle == 0)
     {
@@ -209,7 +260,6 @@ void NUMASupportInitialize()
     }
     if (g_numaHandle != 0)
     {
-        dlsym(g_numaHandle, "numa_allocate_cpumask");
 #define PER_FUNCTION_BLOCK(fn) \
     fn##_ptr = (decltype(fn)*)dlsym(g_numaHandle, #fn); \
     if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from libnuma\n"); abort(); }
index 4fb2bdb6310b274831ea60142a2b142ee89da9ff..4d75508cc7bc640543ba6c8a4fa8f8af1268e58f 100644 (file)
@@ -46,11 +46,56 @@ bool g_numaAvailable = false;
 void* numaHandle = nullptr;
 
 #if HAVE_NUMA_H
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #define PER_FUNCTION_BLOCK(fn) decltype(fn)* fn##_ptr;
 FOR_ALL_NUMA_FUNCTIONS
 #undef PER_FUNCTION_BLOCK
-#endif // HAVE_NUMA_H
 
+#if defined(__linux__)
+static bool ShouldOpenLibNuma()
+{
+    // This is a simple heuristic to determine if libnuma.so should be opened.  There's
+    // no point in linking and resolving everything in this library if we're running on
+    // a system that's not NUMA-capable.
+    int fd = open("/sys/devices/system/node/possible", O_RDONLY | O_CLOEXEC);
+
+    if (fd == -1)
+    {
+        // sysfs might not be mounted, not available, or the interface might have
+        // changed.  Return `true' here so NUMASupportInitialize() can try initializing
+        // NUMA support with libnuma.
+        return true;
+    }
+
+    while (true)
+    {
+        char buffer[32];
+        ssize_t bytesRead = read(fd, buffer, 32);
+
+        if (bytesRead == -1 && errno == EINTR)
+        {
+            continue;
+        }
+
+        close(fd);
+
+        // If an unknown error happened (bytesRead < 0), or the file was empty
+        // (bytesRead = 0), let libnuma handle this.  Otherwise, if there's just
+        // one NUMA node, don't bother linking in libnuma.
+        return (bytesRead <= 0) ? true : strncmp(buffer, "0\n", bytesRead) != 0;
+    }
+}
+#else
+static bool ShouldOpenLibNuma()
+{
+    return true;
+}
+#endif // __linux__
+
+#endif // HAVE_NUMA_H
 
 /*++
 Function:
@@ -65,6 +110,13 @@ BOOL
 NUMASupportInitialize()
 {
 #if HAVE_NUMA_H
+    if (!ShouldOpenLibNuma())
+    {
+        g_numaAvailable = false;
+        g_highestNumaNode = 0;
+        return TRUE;
+    }
+
     numaHandle = dlopen("libnuma.so", RTLD_LAZY);
     if (numaHandle == 0)
     {
@@ -72,7 +124,6 @@ NUMASupportInitialize()
     }
     if (numaHandle != 0)
     {
-        dlsym(numaHandle, "numa_allocate_cpumask");
 #define PER_FUNCTION_BLOCK(fn) \
     fn##_ptr = (decltype(fn)*)dlsym(numaHandle, #fn); \
     if (fn##_ptr == NULL) { fprintf(stderr, "Cannot get symbol " #fn " from libnuma\n"); abort(); }