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]