1 #ifndef NETGEN_CORE_UTILS_HPP
2 #define NETGEN_CORE_UTILS_HPP
3 
4 #include <atomic>
5 #include <chrono>
6 #include <map>
7 #include <ostream>
8 #include <sstream>
9 #include <string>
10 
11 #include "ngcore_api.hpp"       // for NGCORE_API and CPU arch macros
12 
13 #if defined(__APPLE__) && defined(NETGEN_ARCH_ARM64)
14 #include <mach/mach_time.h>
15 #endif
16 
17 #ifdef NETGEN_ARCH_AMD64
18 #ifdef WIN32
19 #include <intrin.h>   // for __rdtsc()  CPU time step counter
20 #else
21 #include <x86intrin.h>   // for __rdtsc()  CPU time step counter
22 #endif // WIN32
23 #endif // NETGEN_ARCH_AMD64
24 
25 namespace ngcore
26 {
27   // MPI rank, nranks TODO: Rename
28   extern NGCORE_API int id, ntasks;
29 
30   NGCORE_API std::string Demangle(const char* typeinfo);
31 
32   template<typename T>
GetName(const T & obj)33   std::string GetName(const T& obj)
34   { return Demangle(typeid(obj).name()); }
35 
36 #if defined(__GNUC__)
likely(bool x)37   inline bool likely (bool x) { return bool(__builtin_expect(long(x), 1L)); }
unlikely(bool x)38   inline bool unlikely (bool x) { return bool(__builtin_expect(long(x), 0L)); }
39 #else
likely(bool x)40   inline bool likely (bool x) { return x; }
unlikely(bool x)41   inline bool unlikely (bool x) { return x; }
42 #endif
43 
44   using TClock = std::chrono::system_clock;
45   extern NGCORE_API const std::chrono::time_point<TClock> wall_time_start;
46 
47   // Time in seconds since program start
WallTime()48   inline double WallTime () noexcept
49   {
50       std::chrono::time_point<TClock> now = TClock::now();
51       std::chrono::duration<double> elapsed_seconds = now-wall_time_start;
52       return elapsed_seconds.count();
53   }
54 
55   // High precision clock counter register
56   using TTimePoint = size_t;
57   extern NGCORE_API double seconds_per_tick;
58 
GetTimeCounter()59   inline TTimePoint GetTimeCounter() noexcept
60   {
61 #if defined(__APPLE__) && defined(NETGEN_ARCH_ARM64)
62     return mach_absolute_time();
63 #elif defined(NETGEN_ARCH_AMD64)
64     return __rdtsc();
65 #elif defined(NETGEN_ARCH_ARM64) && defined(__GNUC__)
66     // __GNUC__ is also defined by CLANG. Use inline asm to read Generic Timer
67     unsigned long long tics;
68     __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (tics));
69     return tics;
70 #else
71 #warning "Unsupported CPU architecture"
72     return 0;
73 #endif
74   }
75 
76   template <class T>
ToString(const T & t)77   inline std::string ToString (const T& t)
78   {
79       std::stringstream ss;
80       ss << t;
81       return ss.str();
82   }
83 
84   template<typename T1, typename T2>
operator <<(std::ostream & ost,const std::map<T1,T2> & map)85   std::ostream& operator << (std::ostream& ost, const std::map<T1,T2>& map)
86   {
87     for(auto& val : map)
88       ost << "\n" << val.first << ": " << val.second;
89     return ost;
90   }
91 
92   template <class T>
Swap(T & a,T & b)93   NETGEN_INLINE void Swap (T & a, T & b)
94   {
95       T temp = std::move(a);
96       a = std::move(b);
97       b = std::move(temp);
98   }
99 
100   // checks if string starts with sequence
StartsWith(const std::string & str,const std::string & start)101   inline bool StartsWith(const std::string& str, const std::string& start)
102   {
103     if(start.size() > str.size())
104       return false;
105     return std::equal(start.begin(), start.end(), str.begin());
106   }
107 
108   // checks if string ends with sequence
EndsWith(const std::string & str,const std::string & end)109   inline bool EndsWith(const std::string& str, const std::string& end)
110   {
111     if(end.size() > str.size())
112       return false;
113     return std::equal(end.rbegin(), end.rend(), str.rbegin());
114   }
115 
116   template<typename T>
AsAtomic(T & d)117   NETGEN_INLINE std::atomic<T> & AsAtomic (T & d)
118   {
119     return reinterpret_cast<std::atomic<T>&> (d);
120   }
121 
AtomicAdd(double & sum,double val)122   NETGEN_INLINE double AtomicAdd( double & sum, double val )
123   {
124       std::atomic<double> & asum = AsAtomic(sum);
125       double current = asum.load();
126       while (!asum.compare_exchange_weak(current, current + val))
127           ;
128       return current;
129   }
130 
131   template<typename T>
AtomicMin(T & minval,T val)132   NETGEN_INLINE T AtomicMin( T & minval, T val )
133   {
134       std::atomic<T> & aminval = AsAtomic(minval);
135       T current = aminval.load();
136       while (!aminval.compare_exchange_weak(current, std::min(current, val)))
137           ;
138       return current;
139   }
140 
141   template<typename T>
AtomicMax(T & maxval,T val)142   NETGEN_INLINE T AtomicMax( T & maxval, T val )
143   {
144       std::atomic<T> & amaxval = AsAtomic(maxval);
145       T current = amaxval.load();
146       while (!amaxval.compare_exchange_weak(current, std::max(current, val)))
147           ;
148       return current;
149   }
150 
151   namespace detail
152   {
153     template<typename T>
154     struct IndexTypeHelper
155     {
156     private:
157       template<typename T2>
checkngcore::detail::IndexTypeHelper158       static constexpr auto check(T2* t) -> typename T2::index_type { return *t; }
159       static constexpr size_t check(...);
160 
161     public:
162       using type = decltype(check((T*) nullptr)); // NOLINT
163     };
164 
165   } // namespace detail
166 
167   // Get index type of object. If object has a typedef index_type it is this type, else size_t
168   template<typename T>
169   using index_type = typename detail::IndexTypeHelper<T>::type;
170 
171   class MyMutex
172   {
173     std::atomic<bool> m;
174   public:
MyMutex()175     MyMutex() { m.store(false, std::memory_order_relaxed); }
lock()176     void lock()
177     {
178       bool should = false;
179       while (!m.compare_exchange_weak(should, true))
180         {
181           should = false;
182 #ifdef NETGEN_ARCH_AMD64
183           _mm_pause();
184 #endif // NETGEN_ARCH_AMD64
185         }
186     }
unlock()187     void unlock()
188     {
189       m = false;
190     }
191   };
192 
193   class MyLock
194   {
195     MyMutex & mutex;
196   public:
MyLock(MyMutex & amutex)197     MyLock (MyMutex & amutex) : mutex(amutex) { mutex.lock(); }
~MyLock()198     ~MyLock () { mutex.unlock(); }
199   };
200 
201 
202 } // namespace ngcore
203 
204 #endif // NETGEN_CORE_UTILS_HPP
205