#ifndef NETGEN_CORE_UTILS_HPP #define NETGEN_CORE_UTILS_HPP #include #include #include #include #include #include #include #include #include "ngcore_api.hpp" // for NGCORE_API and CPU arch macros #if defined(__APPLE__) && defined(NETGEN_ARCH_ARM64) #include #endif #ifdef NETGEN_ARCH_AMD64 #ifdef WIN32 #include // for __rdtsc() CPU time step counter #else #include // for __rdtsc() CPU time step counter #endif // WIN32 #endif // NETGEN_ARCH_AMD64 namespace ngcore { // MPI rank, nranks TODO: Rename // [[deprecated("don't use global id/ntasks")]] extern NGCORE_API int id; // [[deprecated("don't use global id/ntasks")]] extern NGCORE_API int ntasks; NGCORE_API std::string Demangle(const char* typeinfo); template std::string GetName(const T& obj) { return Demangle(typeid(obj).name()); } #if defined(__GNUC__) inline bool likely (bool x) { return bool(__builtin_expect(long(x), 1L)); } inline bool unlikely (bool x) { return bool(__builtin_expect(long(x), 0L)); } #else inline bool likely (bool x) { return x; } inline bool unlikely (bool x) { return x; } #endif using TClock = std::chrono::system_clock; extern NGCORE_API const std::chrono::time_point wall_time_start; // Time in seconds since program start inline double WallTime () noexcept { std::chrono::time_point now = TClock::now(); std::chrono::duration elapsed_seconds = now-wall_time_start; return elapsed_seconds.count(); } // High precision clock counter register using TTimePoint = size_t; extern NGCORE_API double seconds_per_tick; inline TTimePoint GetTimeCounter() noexcept { #if defined(__APPLE__) && defined(NETGEN_ARCH_ARM64) return mach_absolute_time(); #elif defined(NETGEN_ARCH_AMD64) return __rdtsc(); #elif defined(NETGEN_ARCH_ARM64) && defined(__GNUC__) // __GNUC__ is also defined by CLANG. Use inline asm to read Generic Timer unsigned long long tics; __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (tics)); return tics; #elif defined(__EMSCRIPTEN__) return std::chrono::high_resolution_clock::now().time_since_epoch().count(); #else #warning "Unsupported CPU architecture" return 0; #endif } template inline std::string ToString (const T& t) { std::stringstream ss; ss << t; return ss.str(); } inline std::string ToLower( const std::string & s ) { std::string res; res.reserve(s.size()); for(auto & c : s) res.push_back(tolower(c)); return res; } inline std::string ToLower( const std::filesystem::path & p ) { return ToLower(p.string()); } template void SaveBin (std::ostream & ost, const T & val) { const char * cp = reinterpret_cast (&val); for (unsigned j = 0; j < sizeof(T); j++) ost.put(cp[j]); } template void LoadBin (std::istream & ist, T & val) { char * cp = reinterpret_cast (&val); for (unsigned j = 0; j < sizeof(T); j++) ist.get(cp[j]); } template std::ostream& operator << (std::ostream& ost, const std::map& map) { for(auto& val : map) ost << "\n" << val.first << ": " << val.second; return ost; } template NETGEN_INLINE void Swap (T & a, T & b) { T temp = std::move(a); a = std::move(b); b = std::move(temp); } /// min of 2 values template NETGEN_INLINE T min2 (T a, T b) { return (a < b) ? a : b; } /// max of 2 values template NETGEN_INLINE T max2 (T a, T b) { return (a > b) ? a : b; } /// min of 3 values template NETGEN_INLINE T min3 (T a, T b, T c) { return (a < b) ? (a < c) ? a : c : (b < c) ? b : c; } /// max of 3 values template NETGEN_INLINE T max3 (T a, T b, T c) { /// return (a > b) ? ((a > c) ? a : c) : ((b > c) ? b : c); } /// sign of value (+1, 0, -1) template NETGEN_INLINE int sgn (T a) { return (a > 0) ? 1 : ( ( a < 0) ? -1 : 0 ); } /// square element template NETGEN_INLINE T sqr (const T a) { return a * a; } /// element to the third power template NETGEN_INLINE T pow3 (const T a) { return a * a * a; } NETGEN_INLINE double IfPos (double a, double b, double c) { return a>0 ? b : c; } NETGEN_INLINE double IfZero (double a, double b, double c) { return a==0. ? b : c; } // checks if string starts with sequence inline bool StartsWith(const std::string& str, const std::string& start) { if(start.size() > str.size()) return false; return std::equal(start.begin(), start.end(), str.begin()); } // checks if string ends with sequence inline bool EndsWith(const std::string& str, const std::string& end) { if(end.size() > str.size()) return false; return std::equal(end.rbegin(), end.rend(), str.rbegin()); } template NETGEN_INLINE std::atomic & AsAtomic (T & d) { return reinterpret_cast&> (d); } NETGEN_INLINE double AtomicAdd( double & sum, double val ) { std::atomic & asum = AsAtomic(sum); double current = asum.load(); while (!asum.compare_exchange_weak(current, current + val)) ; return current; } template NETGEN_INLINE T AtomicMin( T & minval, T val ) { std::atomic & aminval = AsAtomic(minval); T current = aminval.load(); while (!aminval.compare_exchange_weak(current, std::min(current, val))) ; return current; } template NETGEN_INLINE T AtomicMax( T & maxval, T val ) { std::atomic & amaxval = AsAtomic(maxval); T current = amaxval.load(); while (!amaxval.compare_exchange_weak(current, std::max(current, val))) ; return current; } template using IC = std::integral_constant; // needed for Iterate namespace detail { template struct IsIC_trait { static constexpr auto check() { return false; } }; template struct IsIC_trait> == true, int> > { static constexpr auto check() { return true; } }; } template constexpr bool is_IC() { return detail::IsIC_trait::check(); } template NETGEN_INLINE void Iterate (FUNC f) { if constexpr (NUM > 1) Iterate (f); if constexpr (NUM >= 1) f(IC()); } template NETGEN_INLINE void Switch (size_t nr, FUNC f) { if (NUM-1 == nr) f(IC()); if constexpr (NUM > 1) Switch (nr, f); } namespace detail { template struct IndexTypeHelper { private: template static constexpr auto check(T2* t) -> typename T2::index_type { return *t; } static constexpr size_t check(...); public: using type = decltype(check((T*) nullptr)); // NOLINT }; } // namespace detail // Get index type of object. If object has a typedef index_type it is this type, else size_t template using index_type = typename detail::IndexTypeHelper::type; class MyMutex { std::atomic m; public: MyMutex() { m.store(false, std::memory_order_relaxed); } void lock() { bool should = false; while (!m.compare_exchange_weak(should, true)) { should = false; #ifdef NETGEN_ARCH_AMD64 _mm_pause(); #endif // NETGEN_ARCH_AMD64 } } void unlock() { m = false; } }; class MyLock { MyMutex & mutex; public: MyLock (MyMutex & amutex) : mutex(amutex) { mutex.lock(); } ~MyLock () { mutex.unlock(); } }; NGCORE_API int GetCompiledSIMDSize(); NGCORE_API bool IsRangeCheckEnabled(); NGCORE_API std::filesystem::path GetTempFilename(); NGCORE_API void* GetRawSymbol( std::string func_name ); template TFunc GetSymbol( std::string func_name ) { return reinterpret_cast(GetRawSymbol(func_name)); } // Class to handle/load shared libraries class NGCORE_API SharedLibrary { std::filesystem::path lib_name; std::optional directory_to_delete = std::nullopt; // #ifdef WIN32 // HINSTANCE lib = nullptr; // #else // WIN32 void *lib = nullptr; // #endif // WIN32 public: SharedLibrary() = default; SharedLibrary(const std::filesystem::path & lib_name_, std::optional directory_to_delete_ = std::nullopt, bool global = false ); SharedLibrary(const SharedLibrary &) = delete; SharedLibrary & operator =(const SharedLibrary &) = delete; ~SharedLibrary(); template TFunc GetSymbol( std::string func_name ) { return reinterpret_cast(GetRawSymbol(func_name)); } void Load( const std::filesystem::path & lib_name_, bool global); void Unload(); void* GetRawSymbol( std::string func_name ); }; } // namespace ngcore #endif // NETGEN_CORE_UTILS_HPP