This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-doris.git


The following commit(s) were added to refs/heads/master by this push:
     new d1cb291  [improvement] check simd instructions before start (#8042)
d1cb291 is described below

commit d1cb2913c1da372dd9711569e9e3609a0cb22113
Author: yiguolei <[email protected]>
AuthorDate: Thu Feb 17 10:46:03 2022 +0800

    [improvement] check simd instructions before start (#8042)
    
    Sometimes BE is build on a machine with SIMD instruction such as AVX2.
    But the BE binary will be copied to a machine without AVX2. It will crashed 
without any error message.
    
    This PR will check the required SIMD instructions and print error messages 
during startup.
---
 be/src/common/utils.h         |   2 +
 be/src/service/doris_main.cpp | 199 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+)

diff --git a/be/src/common/utils.h b/be/src/common/utils.h
index 61fed1d..c0ca459 100644
--- a/be/src/common/utils.h
+++ b/be/src/common/utils.h
@@ -21,6 +21,8 @@
 
 namespace doris {
 
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
 struct AuthInfo {
     std::string user;
     std::string passwd;
diff --git a/be/src/service/doris_main.cpp b/be/src/service/doris_main.cpp
index fba3669..6c59b72 100644
--- a/be/src/service/doris_main.cpp
+++ b/be/src/service/doris_main.cpp
@@ -20,8 +20,12 @@
 #include <unistd.h>
 
 #include <condition_variable>
+#include <cstring>
+#include <errno.h>
 #include <mutex>
+#include <setjmp.h>
 #include <thread>
+#include <unistd.h>
 #include <unordered_map>
 
 #if defined(LEAK_SANITIZER)
@@ -40,6 +44,7 @@
 #include "common/logging.h"
 #include "common/resource_tls.h"
 #include "common/status.h"
+#include "common/utils.h"
 #include "env/env.h"
 #include "olap/options.h"
 #include "olap/storage_engine.h"
@@ -74,6 +79,200 @@ static void thrift_output(const char* x) {
 
 } // namespace doris
 
+// These code is referenced from clickhouse
+// It is used to check the SIMD instructions
+enum class InstructionFail {
+    NONE = 0,
+    SSE3 = 1,
+    SSSE3 = 2,
+    SSE4_1 = 3,
+    SSE4_2 = 4,
+    POPCNT = 5,
+    AVX = 6,
+    AVX2 = 7,
+    AVX512 = 8,
+    ARM_NEON = 9
+};
+
+auto instruction_fail_to_string(InstructionFail fail)
+{
+    switch (fail)
+    {
+#define ret(x) return std::make_tuple(STDERR_FILENO, x, ARRAY_SIZE(x) - 1)
+        case InstructionFail::NONE:
+            ret("NONE");
+        case InstructionFail::SSE3:
+            ret("SSE3");
+        case InstructionFail::SSSE3:
+            ret("SSSE3");
+        case InstructionFail::SSE4_1:
+            ret("SSE4.1");
+        case InstructionFail::SSE4_2:
+            ret("SSE4.2");
+        case InstructionFail::POPCNT:
+            ret("POPCNT");
+        case InstructionFail::AVX:
+            ret("AVX");
+        case InstructionFail::AVX2:
+            ret("AVX2");
+        case InstructionFail::AVX512:
+            ret("AVX512");
+        case InstructionFail::ARM_NEON:
+            ret("ARM_NEON");
+    }
+    __builtin_unreachable();
+}
+
+
+sigjmp_buf jmpbuf;
+
+void sig_ill_check_handler(int, siginfo_t *, void *)
+{
+    siglongjmp(jmpbuf, 1);
+}
+
+/// Check if necessary SSE extensions are available by trying to execute some 
sse instructions.
+/// If instruction is unavailable, SIGILL will be sent by kernel.
+void check_required_instructions_impl(volatile InstructionFail & fail)
+{
+#if defined(__SSE3__)
+    fail = InstructionFail::SSE3;
+    __asm__ volatile ("addsubpd %%xmm0, %%xmm0" : : : "xmm0");
+#endif
+
+#if defined(__SSSE3__)
+    fail = InstructionFail::SSSE3;
+    __asm__ volatile ("pabsw %%xmm0, %%xmm0" : : : "xmm0");
+
+#endif
+
+#if defined(__SSE4_1__)
+    fail = InstructionFail::SSE4_1;
+    __asm__ volatile ("pmaxud %%xmm0, %%xmm0" : : : "xmm0");
+#endif
+
+#if defined(__SSE4_2__)
+    fail = InstructionFail::SSE4_2;
+    __asm__ volatile ("pcmpgtq %%xmm0, %%xmm0" : : : "xmm0");
+#endif
+
+    /// Defined by -msse4.2
+#if defined(__POPCNT__)
+    fail = InstructionFail::POPCNT;
+    {
+        uint64_t a = 0;
+        uint64_t b = 0;
+        __asm__ volatile ("popcnt %1, %0" : "=r"(a) :"r"(b) :);
+    }
+#endif
+
+#if defined(__AVX__)
+    fail = InstructionFail::AVX;
+    __asm__ volatile ("vaddpd %%ymm0, %%ymm0, %%ymm0" : : : "ymm0");
+#endif
+
+#if defined(__AVX2__)
+    fail = InstructionFail::AVX2;
+    __asm__ volatile ("vpabsw %%ymm0, %%ymm0" : : : "ymm0");
+#endif
+
+#if defined(__AVX512__)
+    fail = InstructionFail::AVX512;
+    __asm__ volatile ("vpabsw %%zmm0, %%zmm0" : : : "zmm0");
+#endif
+
+#if defined(__ARM_NEON__)
+    fail = InstructionFail::ARM_NEON;
+    __asm__ volatile ("vadd.i32  q8, q8, q8" : : : "q8");
+#endif
+
+    fail = InstructionFail::NONE;
+}
+
+bool write_retry(int fd, const char * data, size_t size)
+{
+    if (!size)
+        size = strlen(data);
+
+    while (size != 0)
+    {
+        ssize_t res = ::write(fd, data, size);
+
+        if ((-1 == res || 0 == res) && errno != EINTR)
+            return false;
+
+        if (res > 0)
+        {
+            data += res;
+            size -= res;
+        }
+    }
+
+    return true;
+}
+
+/// Macros to avoid using strlen(), since it may fail if SSE is not supported.
+#define WRITE_ERROR(data) do \
+    { \
+        static_assert(__builtin_constant_p(data)); \
+        if (!write_retry(STDERR_FILENO, data, ARRAY_SIZE(data) - 1)) \
+            _Exit(1); \
+    } while (false)
+
+/// Check SSE and others instructions availability. Calls exit on fail.
+/// This function must be called as early as possible, even before main, 
because static initializers may use unavailable instructions.
+void check_required_instructions()
+{
+    struct sigaction sa{};
+    struct sigaction sa_old{};
+    sa.sa_sigaction = sig_ill_check_handler;
+    sa.sa_flags = SA_SIGINFO;
+    auto signal = SIGILL;
+    if (sigemptyset(&sa.sa_mask) != 0
+        || sigaddset(&sa.sa_mask, signal) != 0
+        || sigaction(signal, &sa, &sa_old) != 0)
+    {
+        /// You may wonder about strlen.
+        /// Typical implementation of strlen is using SSE4.2 or AVX2.
+        /// But this is not the case because it's compiler builtin and is 
executed at compile time.
+
+        WRITE_ERROR("Can not set signal handler\n");
+        _Exit(1);
+    }
+
+    volatile InstructionFail fail = InstructionFail::NONE;
+
+    if (sigsetjmp(jmpbuf, 1))
+    {
+        WRITE_ERROR("Instruction check fail. The CPU does not support ");
+        if (!std::apply(write_retry, instruction_fail_to_string(fail)))
+            _Exit(1);
+        WRITE_ERROR(" instruction set.\n");
+        WRITE_ERROR("For example, if your CPU does not support AVX2, you need 
to rebuild the Doris BE with: USE_AVX2=0 sh build.sh --be");
+        _Exit(1);
+    }
+
+    check_required_instructions_impl(fail);
+
+    if (sigaction(signal, &sa_old, nullptr))
+    {
+        WRITE_ERROR("Can not set signal handler\n");
+        _Exit(1);
+    }
+}
+
+struct Checker
+{
+    Checker()
+    {
+        check_required_instructions();
+    }
+} checker
+#ifndef __APPLE__
+    __attribute__((init_priority(101)))    /// Run before other static 
initializers.
+#endif
+;
+
 int main(int argc, char** argv) {
 
     // check if print version or help

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to