Add Fastor library
This commit is contained in:
99
noarch/include/Fastor/util/extended_algorithms.h
Normal file
99
noarch/include/Fastor/util/extended_algorithms.h
Normal file
@@ -0,0 +1,99 @@
|
||||
#ifndef EXTENDED_ALGORITHMS_H
|
||||
#define EXTENDED_ALGORITHMS_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <numeric>
|
||||
#include <cstring>
|
||||
|
||||
#if defined(FASTOR_UNIX_OS)
|
||||
#include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
// Implementation of STL iota to work on other types such as
|
||||
// std::complex. For std::complex iota_impl increments the real
|
||||
// part only
|
||||
template<class ForwardIt, class T>
|
||||
inline void iota_impl(ForwardIt first, ForwardIt last, T value)
|
||||
{
|
||||
while(first != last) {
|
||||
*first++ = value;
|
||||
value += T(1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, size_t N>
|
||||
inline std::array<int,N> argsort(const std::array<T,N> &v) {
|
||||
std::array<int,N> idx;
|
||||
std::iota(idx.begin(),idx.end(),0);
|
||||
std::sort(idx.begin(), idx.end(), [&v](int i1, int i2) {return v[i1] < v[i2];});
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
template<typename T, size_t N, typename std::enable_if<std::is_arithmetic<T>::value,bool>::type = 0>
|
||||
inline std::string itoa(const std::array<T,N>& arr) {
|
||||
std::string out = std::to_string(arr[0]);
|
||||
for (size_t i=1; i<N; ++i)
|
||||
out += ","+std::to_string(arr[i]);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
#if defined(FASTOR_UNIX_OS)
|
||||
inline size_t set_stack_size(size_t size) {
|
||||
// If the function does not work, copy-paste it within
|
||||
// the body of the main
|
||||
|
||||
// size is stack size in MB (for instance provide 80 for 80MB)
|
||||
// returns old stack size in MB
|
||||
const rlim_t stacksize = size*1024*1024;
|
||||
struct rlimit rl;
|
||||
int result;
|
||||
result = getrlimit(RLIMIT_STACK, &rl);
|
||||
rlim_t old = rl.rlim_cur = stacksize;
|
||||
if (result==0) {
|
||||
if (rl.rlim_cur < stacksize) {
|
||||
rl.rlim_cur = stacksize;
|
||||
result = setrlimit(RLIMIT_STACK,&rl);
|
||||
FASTOR_ASSERT(result !=0, "CHANGING STACK SIZE FAILED");
|
||||
}
|
||||
}
|
||||
|
||||
return old;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Get sign of a number
|
||||
template <typename T>
|
||||
inline constexpr int signum(T x, [[gnu::unused]] std::false_type is_signed) {
|
||||
return T(0) < x;
|
||||
}
|
||||
template <typename T>
|
||||
inline constexpr int signum(T x, [[gnu::unused]] std::true_type is_signed) {
|
||||
return (T(0) < x) - (x < T(0));
|
||||
}
|
||||
template <typename T>
|
||||
inline constexpr int signum(T x) {
|
||||
return signum(x, std::is_signed<T>());
|
||||
}
|
||||
|
||||
// Get a string +/- based on sign
|
||||
template <typename T> std::string signum_string(T val) {
|
||||
return signum(val) == 1 ? "+" : "-";
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace Fastor
|
||||
|
||||
|
||||
#endif // EXTENDED_ALGORITHMS_H
|
||||
|
||||
219
noarch/include/Fastor/util/print.h
Normal file
219
noarch/include/Fastor/util/print.h
Normal file
@@ -0,0 +1,219 @@
|
||||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef FASTOR_SSE2_IMPL
|
||||
#include <emmintrin.h>
|
||||
#endif
|
||||
#ifdef FASTOR_AVX_IMPL
|
||||
#include <immintrin.h>
|
||||
#endif
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
|
||||
//! IOFormat class for tensors
|
||||
struct IOFormat {
|
||||
inline IOFormat(
|
||||
int precision,
|
||||
const std::string& colsep,
|
||||
const std::string& rowsep,
|
||||
const std::string& rowprefix,
|
||||
const std::string& rowsuffix,
|
||||
bool print_dimensions
|
||||
) {
|
||||
_precision = precision;
|
||||
_colsep = colsep;
|
||||
_rowsep = rowsep;
|
||||
_rowprefix = rowprefix;
|
||||
_rowsuffix = rowsuffix;
|
||||
_print_dimensions = print_dimensions;
|
||||
}
|
||||
|
||||
int _precision;
|
||||
std::string _colsep;
|
||||
std::string _rowsep;
|
||||
std::string _rowprefix;
|
||||
std::string _rowsuffix;
|
||||
bool _print_dimensions;
|
||||
};
|
||||
|
||||
|
||||
#ifndef FASTOR_DEFINE_IO_FORMAT
|
||||
#define FASTOR_DEFINE_IO_FORMAT {std::numeric_limits<double>::digits10,", ","\n","[","]",true};
|
||||
// #define FASTOR_DEFINE_IO_FORMAT IOFormat(9,",","\n","[","]",true);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef FASTOR_SSE2_IMPL
|
||||
inline void print(__m128 b) {
|
||||
const float* a = (const float*)&b;
|
||||
std::cout << a[0] << " " << a[1] << " " << a[2] << " " << a[3] << '\n';
|
||||
}
|
||||
inline void print(__m128d b) {
|
||||
const double* a = (const double*)&b;
|
||||
std::cout << a[0] << " " << a[1] << '\n';
|
||||
}
|
||||
#endif
|
||||
#ifdef FASTOR_AVX_IMPL
|
||||
inline void print(__m256 b) {
|
||||
const float* a = (const float*)&b;
|
||||
std::cout << a[0] << " " << a[1] << " " << a[2] << " " << a[3] << " " <<
|
||||
a[4] << " " << a[5] << " " << a[6] << " " << a[7] << '\n';
|
||||
}
|
||||
inline void print(__m256d b) {
|
||||
const double* a = (const double*)&b;
|
||||
std::cout << a[0] << " " << a[1] << " " << a[2] << " " << a[3] << '\n';
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
inline void print(const std::vector<T> &v) {
|
||||
for (auto &k: v) {
|
||||
std::cout << k << '\n';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
inline void print(const std::array<T,N> &arr) {
|
||||
for (std::size_t i=0; i<N; i++) {
|
||||
std::cout << arr[i] << '\n';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void print(const std::vector<std::vector<T>> &arr) {
|
||||
for (std::size_t i=0; i<arr.size(); i++) {
|
||||
for (std::size_t j=0; j<arr[i].size(); j++) {
|
||||
std::cout << arr[i][j] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t M>
|
||||
inline void print(const std::vector<std::array<T,M>> &arr) {
|
||||
for (std::size_t i=0; i<arr.size(); i++) {
|
||||
for (std::size_t j=0; j<M; j++) {
|
||||
std::cout << arr[i][j] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<typename T, std::size_t M, std::size_t N>
|
||||
inline void print(const std::array<std::array<T,M>,N> &arr) {
|
||||
for (std::size_t i=0; i<N; i++) {
|
||||
for (std::size_t j=0; j<M; j++) {
|
||||
std::cout << arr[i][j] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template<typename T,std::size_t N>
|
||||
inline void print(const T *arr) {
|
||||
for (std::size_t i=0; i<N; i++) {
|
||||
std::cout << arr[i] << '\n';
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline void print(const T &a) {
|
||||
std::cout << a << '\n';
|
||||
}
|
||||
|
||||
template<typename T, typename ... Rest>
|
||||
inline void print(const T &first, const Rest& ... rest) {
|
||||
print(first);
|
||||
print(rest...);
|
||||
}
|
||||
|
||||
inline void print() {
|
||||
std::cout << '\n';
|
||||
}
|
||||
/*--------------------------------------*/
|
||||
|
||||
|
||||
|
||||
// Print horizontally
|
||||
/*--------------------------------------*/
|
||||
template<typename T>
|
||||
inline void println(const std::vector<T> &v) {
|
||||
for (auto &k: v) {
|
||||
std::cout << k << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
template<typename T, std::size_t N>
|
||||
inline void println(const std::array<T,N> &arr) {
|
||||
for (std::size_t i=0; i<N; i++) {
|
||||
std::cout << arr[i] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
template<typename T,std::size_t N>
|
||||
inline void println(const T *arr) {
|
||||
for (std::size_t i=0; i<N; i++) {
|
||||
std::cout << arr[i] << " ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline void println(const T &a) {
|
||||
std::cout << a;
|
||||
}
|
||||
|
||||
template<typename T, typename ... Rest>
|
||||
inline void println(const T &first, const Rest& ... rest) {
|
||||
println(first);
|
||||
std::cout << ' ';
|
||||
println(rest...);
|
||||
}
|
||||
|
||||
inline void println() {
|
||||
std::cout << ' ';
|
||||
}
|
||||
/*--------------------------------------*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Warn
|
||||
/*--------------------------------------*/
|
||||
template<typename T>
|
||||
inline void warn(const T &a) {
|
||||
std::cerr << a << '\n';
|
||||
}
|
||||
|
||||
template<typename T, typename ... Rest>
|
||||
inline void warn(const T &first, const Rest& ... rest) {
|
||||
warn(first);
|
||||
warn(rest...);
|
||||
}
|
||||
|
||||
|
||||
} // end of namespace
|
||||
|
||||
#endif
|
||||
499
noarch/include/Fastor/util/timeit.h
Normal file
499
noarch/include/Fastor/util/timeit.h
Normal file
@@ -0,0 +1,499 @@
|
||||
#ifndef TIMEIT_H
|
||||
#define TIMEIT_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#include "Fastor/config/cpuid.h"
|
||||
|
||||
|
||||
#ifndef FASTOR_NO_COLOUR_PRINT
|
||||
|
||||
/* FOREGROUND */
|
||||
#define RST "\x1B[0m"
|
||||
#define KRED "\x1B[31m"
|
||||
#define KGRN "\x1B[32m"
|
||||
#define KYEL "\x1B[33m"
|
||||
#define KBLU "\x1B[34m"
|
||||
#define KMAG "\x1B[35m"
|
||||
#define KCYN "\x1B[36m"
|
||||
#define KWHT "\x1B[37m"
|
||||
|
||||
#define FRED(x) KRED x RST
|
||||
#define FGRN(x) KGRN x RST
|
||||
#define FYEL(x) KYEL x RST
|
||||
#define FBLU(x) KBLU x RST
|
||||
#define FMAG(x) KMAG x RST
|
||||
#define FCYN(x) KCYN x RST
|
||||
#define FWHT(x) KWHT x RST
|
||||
|
||||
#define BOLD(x) "\x1B[1m" x RST
|
||||
#define UNDL(x) "\x1B[4m" x RST
|
||||
|
||||
#else
|
||||
|
||||
#define RST
|
||||
#define KRED
|
||||
#define KGRN
|
||||
#define KYEL
|
||||
#define KBLU
|
||||
#define KMAG
|
||||
#define KCYN
|
||||
#define KWHT
|
||||
|
||||
#define FRED(x) x
|
||||
#define FGRN(x) x
|
||||
#define FYEL(x) x
|
||||
#define FBLU(x) x
|
||||
#define FMAG(x) x
|
||||
#define FCYN(x) x
|
||||
#define FWHT(x) x
|
||||
|
||||
#define BOLD(x) x
|
||||
#define UNDL(x) x
|
||||
|
||||
|
||||
#endif // FASTOR_NO_COLOUR_PRINT
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
|
||||
#ifndef FASTOR_USE_RDTSC
|
||||
#define FASTOR_USE_RDTSC
|
||||
#endif
|
||||
|
||||
#ifndef FASTOR_SIMPLE_RDTSC
|
||||
#define FASTOR_SIMPLE_RDTSC
|
||||
#endif
|
||||
|
||||
#ifndef FASTOR_RDTSC_OVERHEAD
|
||||
#define FASTOR_RDTSC_OVERHEAD 0UL
|
||||
#endif
|
||||
|
||||
#ifndef FASTOR_BENCH_RUNTIME
|
||||
#define FASTOR_BENCH_RUNTIME 1.0
|
||||
#endif
|
||||
|
||||
|
||||
// Get cpu cycle count
|
||||
#ifdef _WIN32
|
||||
inline uint64_t rdtsc() {
|
||||
return __rdtsc();
|
||||
}
|
||||
inline uint64_t rdtsc_begin() {
|
||||
return __rdtsc();
|
||||
}
|
||||
inline uint64_t rdtsc_end() {
|
||||
return __rdtsc();
|
||||
}
|
||||
// Linux/GCC
|
||||
#else
|
||||
inline uint64_t rdtsc() {
|
||||
unsigned int lo, hi;
|
||||
// This does not clobber the register so rdtsc overwrites
|
||||
// the register, see
|
||||
// How to Benchmark Code Execution Times on Intel® IA-32
|
||||
// and IA-64 Instruction Set Architectures pp-9
|
||||
// https://intel.ly/3dXFfQN
|
||||
#ifdef FASTOR_SIMPLE_RDTSC
|
||||
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
#else
|
||||
// Use this instead
|
||||
__asm__ __volatile__ ("RDTSC\n\t"
|
||||
"mov %%edx, %0\n\t"
|
||||
"mov %%eax, %1\n\t": "=r" (hi), "=r" (lo) ::
|
||||
// we need to clobber
|
||||
// "%eax", "%edx" // IA-32
|
||||
"%rax", "%rdx" // IA-64
|
||||
);
|
||||
#endif
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
|
||||
#ifndef FASTOR_SIMPLE_RDTSC
|
||||
// There is still one problem with the function above [rdtsc()]
|
||||
// and that is it does not take care of cpu's out-of-order
|
||||
// executation. In order to serialise we make a call to cpuid
|
||||
// just before rdtsc. While this does not effect timing of the
|
||||
// function itself, it introduces a lot of overhead when called
|
||||
// multiple times within a loop
|
||||
// https://intel.ly/3dXFfQN
|
||||
inline uint64_t rdtsc_begin() {
|
||||
unsigned int lo, hi;
|
||||
__asm__ __volatile__ ("CPUID\n\t"
|
||||
"RDTSC\n\t"
|
||||
"mov %%edx, %0\n\t"
|
||||
"mov %%eax, %1\n\t": "=r" (hi), "=r" (lo) ::
|
||||
"%rax", "%rbx", "%rcx", "%rdx" // clobber memory
|
||||
);
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
// and then a call to cpuid immediately after rdtsc
|
||||
inline uint64_t rdtsc_end() {
|
||||
unsigned int lo, hi;
|
||||
__asm__ __volatile__("RDTSCP\n\t"
|
||||
"mov %%edx, %0\n\t"
|
||||
"mov %%eax, %1\n\t"
|
||||
"CPUID\n\t": "=r" (hi), "=r" (lo)::
|
||||
"%rax", "%rbx", "%rcx", "%rdx" // clobber memory
|
||||
);
|
||||
return ((uint64_t)hi << 32) | lo;
|
||||
}
|
||||
#else
|
||||
inline uint64_t rdtsc_begin() { return rdtsc();}
|
||||
inline uint64_t rdtsc_end() { return rdtsc();}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
namespace useless {
|
||||
inline
|
||||
double format_time(double _time) {
|
||||
if (_time >= 1.0e-3 && _time < 1.) return _time / 1e-3;
|
||||
else if (_time >= 1.0e-6 && _time < 1.0e-3) return _time / 1e-6;
|
||||
else if (_time < 1.0e-6) return _time / 1e-9;
|
||||
else return _time;
|
||||
}
|
||||
|
||||
inline
|
||||
std::string format_time_string(double _time) {
|
||||
if (_time >= 1.0e-3 && _time < 1.) return " ms";
|
||||
#ifdef _WIN32
|
||||
else if (_time >= 1.0e-6 && _time < 1.0e-3) return " us";
|
||||
#else
|
||||
else if (_time >= 1.0e-6 && _time < 1.0e-3) return " \xC2\xB5s";
|
||||
#endif
|
||||
else if (_time < 1.0e-6) return " ns";
|
||||
else return " s";
|
||||
}
|
||||
}
|
||||
|
||||
#define FASTOR_FORMAT_BENCH_TIME_DISPLAY_1()\
|
||||
std::cout << counter\
|
||||
<< FGRN(BOLD(" runs, min time: "))\
|
||||
<< std::setprecision(6) << useless::format_time(best_time) << useless::format_time_string(best_time) << ". " \
|
||||
<< FGRN(BOLD("mean time: "))\
|
||||
<< useless::format_time(mean_time) << useless::format_time_string(mean_time) << ". "\
|
||||
<< FGRN(BOLD("max time: "))\
|
||||
<< useless::format_time(worst_time) << useless::format_time_string(worst_time) << ". "\
|
||||
<< FGRN(BOLD("Average no of RDTSC CPU cycles "))\
|
||||
<< uint64_t(cycles/(1.0*counter)) << std::endl;\
|
||||
|
||||
#define FASTOR_FORMAT_BENCH_TIME_DISPLAY_2()\
|
||||
std::cout << counter\
|
||||
<< FGRN(BOLD(" runs, min time: "))\
|
||||
<< std::setprecision(6) << useless::format_time(best_time) << useless::format_time_string(best_time) << ". " \
|
||||
<< FGRN(BOLD("mean time: "))\
|
||||
<< useless::format_time(mean_time) << useless::format_time_string(mean_time) << ". "\
|
||||
<< FGRN(BOLD("max time: "))\
|
||||
<< useless::format_time(worst_time) << useless::format_time_string(worst_time) << std::endl;\
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename T, typename ... Params, typename ... Args>
|
||||
inline
|
||||
std::tuple<double,double,double> // min, mean, max times
|
||||
timeit(T (*func)(Params...), Args...args)
|
||||
{
|
||||
uint64_t counter = 1;
|
||||
double accum_time = 0.0;
|
||||
double mean_time = 0.0;
|
||||
double best_time = std::numeric_limits<double>::max();
|
||||
double worst_time = 0.0;
|
||||
#if defined(FASTOR_USE_RDTSC)
|
||||
uint64_t cycles = 0;
|
||||
CPUID cpuID(0);
|
||||
// A cycle is 1 second per max cpu frequency assuming constant_tsc
|
||||
// Caution: in theory, there is no guarantee that rdtsc would have
|
||||
// strong relation to CPU cycles
|
||||
// https://stackoverflow.com/questions/36663379/seconds-calculation-using-rdtsc
|
||||
double tsc_to_time = 1.0/cpuID.EBX();
|
||||
|
||||
// rdtsc_begin();
|
||||
// rdtsc_end();
|
||||
#endif
|
||||
|
||||
// We collect many samples
|
||||
constexpr uint64_t num_samples = 100UL;
|
||||
// Every sample is composed of multiple runs
|
||||
constexpr uint64_t num_run_per_sample = 5E6;
|
||||
|
||||
auto __min_element = [&](std::array<double,num_samples> &a) {
|
||||
double v = a[0];
|
||||
for (uint64_t n = 1; n < a.size(); n++) {
|
||||
if ((a[n] < v && (a[n] != 0 || a[n] > 1E-14) ) || v == 0) v = a[n];
|
||||
}
|
||||
return v;
|
||||
};
|
||||
|
||||
std::array<double,num_samples> best_times;
|
||||
std::fill(best_times.begin(),best_times.end(),std::numeric_limits<double>::max());
|
||||
std::array<double,num_samples> mean_times = {};
|
||||
std::array<double,num_samples> worst_times = {};
|
||||
|
||||
uint64_t num_collected_samples = 0;
|
||||
|
||||
for (uint64_t sample = 0; sample < num_samples && accum_time < FASTOR_BENCH_RUNTIME; ++sample) {
|
||||
|
||||
for (uint64_t run=0; run < num_run_per_sample; ++run)
|
||||
{
|
||||
#if defined(FASTOR_USE_SYSTEM_CLOCK)
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#elif defined(FASTOR_USE_RDTSC)
|
||||
auto cycle = rdtsc_begin();
|
||||
#else
|
||||
std::chrono::time_point<std::chrono::steady_clock> start, end;
|
||||
start = std::chrono::steady_clock::now();
|
||||
#endif
|
||||
// Run the function
|
||||
func(std::forward<Params>(args)...);
|
||||
// Ignore the few first runs for cache hot measurements
|
||||
if (run < 1 && sample < 1) continue;
|
||||
|
||||
#if defined(FASTOR_USE_RDTSC)
|
||||
cycle = rdtsc_end() - cycle - FASTOR_RDTSC_OVERHEAD;
|
||||
cycles += cycle;
|
||||
double elapsed_t = tsc_to_time*cycle;
|
||||
if (elapsed_t < best_times[sample] && (elapsed_t != 0.0 || elapsed_t > 1E-15)) {
|
||||
best_times[sample] = elapsed_t;
|
||||
};
|
||||
if (elapsed_t > worst_times[sample]) {
|
||||
worst_times[sample] = elapsed_t;
|
||||
};
|
||||
mean_times[sample] += elapsed_t;
|
||||
accum_time += elapsed_t;
|
||||
|
||||
#elif defined(FASTOR_USE_SYSTEM_CLOCK)
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end-start;
|
||||
|
||||
if (elapsed_seconds.count() < best_times[sample] && elapsed_seconds.count() != 0.0) {
|
||||
best_times[sample] = elapsed_seconds.count();
|
||||
};
|
||||
if (elapsed_seconds.count() > worst_times[sample]) {
|
||||
worst_times[sample] = elapsed_seconds.count();
|
||||
};
|
||||
mean_times[sample] += elapsed_seconds.count();
|
||||
accum_time += elapsed_seconds.count();
|
||||
#else
|
||||
end = std::chrono::steady_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end-start;
|
||||
|
||||
if (elapsed_seconds.count() < best_times[sample] && elapsed_seconds.count() != 0.0) {
|
||||
best_times[sample] = elapsed_seconds.count();
|
||||
};
|
||||
if (elapsed_seconds.count() > worst_times[sample]) {
|
||||
worst_times[sample] = elapsed_seconds.count();
|
||||
};
|
||||
mean_times[sample] += elapsed_seconds.count();
|
||||
accum_time += elapsed_seconds.count();
|
||||
#endif
|
||||
counter++;
|
||||
|
||||
if (accum_time > FASTOR_BENCH_RUNTIME)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
num_collected_samples++;
|
||||
}
|
||||
|
||||
best_time = __min_element(best_times);
|
||||
worst_time = *std::max_element(worst_times.begin(),worst_times.begin()+num_collected_samples);
|
||||
mean_time = std::accumulate(mean_times.begin(), mean_times.begin()+num_collected_samples, 0.0);
|
||||
|
||||
mean_time /= (double)counter;
|
||||
#if defined(FASTOR_USE_RDTSC)
|
||||
FASTOR_FORMAT_BENCH_TIME_DISPLAY_1()
|
||||
#else
|
||||
FASTOR_FORMAT_BENCH_TIME_DISPLAY_2()
|
||||
#endif
|
||||
|
||||
return std::make_tuple(best_time, mean_time, worst_time);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// One loop timer
|
||||
template<typename T, typename ... Params, typename ... Args>
|
||||
inline
|
||||
std::tuple<double,double,double> // min, mean, max times
|
||||
timeit(T (*func)(Params...), Args...args)
|
||||
{
|
||||
uint64_t counter = 1;
|
||||
double mean_time = 0.0;
|
||||
double best_time = std::numeric_limits<double>::max();
|
||||
double worst_time = 0.0;
|
||||
#if defined(FASTOR_USE_RDTSC)
|
||||
uint64_t cycles = 0;
|
||||
CPUID cpuID(0);
|
||||
// A cycle is 1 second per max cpu frequency assuming constant_tsc
|
||||
// Caution: in theory, there is no guarantee that rdtsc would have
|
||||
// strong relation to CPU cycles
|
||||
// https://stackoverflow.com/questions/36663379/seconds-calculation-using-rdtsc
|
||||
double tsc_to_time = 1.0/cpuID.EBX();
|
||||
|
||||
// rdtsc_begin();
|
||||
// rdtsc_end();
|
||||
#endif
|
||||
|
||||
for (auto iter=0; iter<1e09; ++iter)
|
||||
{
|
||||
#if defined(FASTOR_USE_SYSTEM_CLOCK)
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
#elif defined(FASTOR_USE_RDTSC)
|
||||
auto cycle = rdtsc_begin();
|
||||
#else
|
||||
std::chrono::time_point<std::chrono::steady_clock> start, end;
|
||||
start = std::chrono::steady_clock::now();
|
||||
#endif
|
||||
|
||||
// Run the function
|
||||
func(std::forward<Params>(args)...);
|
||||
// Ignore the few first runs for cache hot measurements
|
||||
if (iter < 1) continue;
|
||||
|
||||
#if defined(FASTOR_USE_RDTSC)
|
||||
cycle = rdtsc_end() - cycle;
|
||||
cycles += cycle;
|
||||
|
||||
double elapsed_t = tsc_to_time*cycle;
|
||||
if (elapsed_t < best_time && elapsed_t != 0.0) {
|
||||
best_time = elapsed_t;
|
||||
};
|
||||
if (elapsed_t > worst_time) {
|
||||
worst_time = elapsed_t;
|
||||
};
|
||||
mean_time += elapsed_t;
|
||||
#else
|
||||
end = std::chrono::steady_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end-start;
|
||||
|
||||
if (elapsed_seconds.count() < best_time && elapsed_seconds.count() != 0.0) {
|
||||
best_time = elapsed_seconds.count();
|
||||
};
|
||||
if (elapsed_seconds.count() > worst_time) {
|
||||
worst_time = elapsed_seconds.count();
|
||||
};
|
||||
mean_time += elapsed_seconds.count();
|
||||
#endif
|
||||
counter++;
|
||||
|
||||
if (mean_time > FASTOR_BENCH_RUNTIME)
|
||||
{
|
||||
mean_time /= (double)counter;
|
||||
#if defined(FASTOR_USE_RDTSC)
|
||||
FASTOR_FORMAT_BENCH_TIME_DISPLAY_1()
|
||||
#else
|
||||
FASTOR_FORMAT_BENCH_TIME_DISPLAY_2()
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple(best_time, mean_time, worst_time);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// timeit with return values
|
||||
template<typename T, typename ... Params, typename ... Args>
|
||||
inline std::tuple<double,uint64_t> rtimeit(T (*func)(Params...), Args...args)
|
||||
{
|
||||
uint64_t counter = 1;
|
||||
double mean_time = 0.0;
|
||||
double best_time = std::numeric_limits<double>::max();
|
||||
double worst_time = 0;
|
||||
uint64_t cycles = 0;
|
||||
|
||||
for (auto iter=0; iter<1e09; ++iter)
|
||||
{
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
start = std::chrono::system_clock::now();
|
||||
auto cycle = rdtsc();
|
||||
|
||||
// Run the function
|
||||
func(std::forward<Params>(args)...);
|
||||
// Ignore the few first runs for cache hot measurements
|
||||
if (iter < 1) continue;
|
||||
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end-start;
|
||||
cycle = rdtsc() - cycle;
|
||||
cycles += cycle;
|
||||
|
||||
mean_time += elapsed_seconds.count();
|
||||
|
||||
if (elapsed_seconds.count() < best_time && elapsed_seconds.count() != 0) {
|
||||
best_time = elapsed_seconds.count();
|
||||
};
|
||||
if (elapsed_seconds.count() > worst_time) {
|
||||
worst_time = elapsed_seconds.count();
|
||||
};
|
||||
|
||||
counter++;
|
||||
|
||||
if (mean_time > FASTOR_BENCH_RUNTIME)
|
||||
{
|
||||
mean_time /= counter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return std::make_tuple(mean_time,uint64_t(cycles/(1.0*counter)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// tic toc
|
||||
template<typename T=double>
|
||||
struct timer
|
||||
{
|
||||
inline void tic() {t0 = std::chrono::steady_clock::now();}
|
||||
|
||||
inline T toc(const std::string &msg="") {
|
||||
using namespace std::chrono;
|
||||
elapsed = steady_clock::now() - t0;
|
||||
T elapsed_seconds = duration<T,seconds::period>(elapsed).count();
|
||||
if (msg.empty()) std::cout << FGRN(BOLD("Elapsed time is: ")) <<
|
||||
elapsed_seconds << FGRN(BOLD(" seconds \n"));
|
||||
else std::cout << std::string("\x1B[32m ")+std::string("\x1B[1m")+msg+std::string("\x1B[0m")+" "
|
||||
<< elapsed_seconds << FGRN(BOLD(" seconds \n"));
|
||||
return elapsed_seconds;
|
||||
}
|
||||
|
||||
std::chrono::steady_clock::time_point t0;
|
||||
std::chrono::steady_clock::duration elapsed;
|
||||
};
|
||||
|
||||
|
||||
// Define a no operation function
|
||||
inline void no_op(){}
|
||||
|
||||
} // end of namespace
|
||||
|
||||
|
||||
#endif
|
||||
84
noarch/include/Fastor/util/types.h
Normal file
84
noarch/include/Fastor/util/types.h
Normal file
@@ -0,0 +1,84 @@
|
||||
#ifndef TYPE_NAMES_H
|
||||
#define TYPE_NAMES_H
|
||||
|
||||
#if __cplusplus >= 201703L
|
||||
|
||||
#include <string_view>
|
||||
namespace Fastor {
|
||||
namespace useless {
|
||||
class probe_type;
|
||||
inline void extract_type(std::string_view& name, std::string_view probe_type_name);
|
||||
} // useless
|
||||
|
||||
template <typename T>
|
||||
constexpr std::string_view type_name() {
|
||||
std::string_view probe_type_name("class useless::probe_type");
|
||||
const std::string_view class_specifier("class ");
|
||||
|
||||
std::string_view name;
|
||||
#ifdef __clang__
|
||||
name = __PRETTY_FUNCTION__;
|
||||
probe_type_name.remove_prefix (class_specifier.length ());
|
||||
#elif defined(__GNUC__)
|
||||
name = __PRETTY_FUNCTION__;
|
||||
probe_type_name.remove_prefix (class_specifier.length ());
|
||||
#elif defined(_MSC_VER)
|
||||
name = __FUNCSIG__;
|
||||
#endif
|
||||
useless::extract_type(name, probe_type_name);
|
||||
return name;
|
||||
}
|
||||
|
||||
namespace useless {
|
||||
inline void extract_type(std::string_view& name, std::string_view probe_type_name) {
|
||||
if (name.find(probe_type_name) == std::string_view::npos) {
|
||||
//For known type probe_type get raw name and then prefix and suffix sizes
|
||||
const std::string_view probe_type_raw_name = type_name<probe_type> ();
|
||||
|
||||
const size_t prefix_size = probe_type_raw_name.find(probe_type_name);
|
||||
const size_t suffix_size = probe_type_raw_name.length () - prefix_size - probe_type_name.length();
|
||||
|
||||
name.remove_prefix (prefix_size);
|
||||
name.remove_suffix (suffix_size);
|
||||
}
|
||||
}
|
||||
} // useless
|
||||
} // end of namespace Fastor
|
||||
|
||||
#else
|
||||
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
#if defined(__GNUC__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
namespace Fastor {
|
||||
template <class T>
|
||||
std::string type_name()
|
||||
{
|
||||
typedef typename std::remove_reference<T>::type TR;
|
||||
std::unique_ptr<char, void(*)(void*)> own(nullptr, std::free);
|
||||
#if defined(__GNUC__)
|
||||
int status = 0;
|
||||
char* demangled = abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, &status);
|
||||
std::string r = own != nullptr ? own.get() : std::string(demangled);
|
||||
#else
|
||||
std::string r = own != nullptr ? own.get() : typeid(TR).name();
|
||||
#endif
|
||||
if (std::is_const<TR>::value)
|
||||
r += " const";
|
||||
if (std::is_volatile<TR>::value)
|
||||
r += " volatile";
|
||||
if (std::is_lvalue_reference<T>::value)
|
||||
r += "&";
|
||||
else if (std::is_rvalue_reference<T>::value)
|
||||
r += "&&";
|
||||
return r;
|
||||
}
|
||||
} // end of namespace Fastor
|
||||
|
||||
#endif
|
||||
#endif // TYPE_NAMES_H
|
||||
9
noarch/include/Fastor/util/util.h
Normal file
9
noarch/include/Fastor/util/util.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#ifndef UTILS_H
|
||||
#define UTILS_H
|
||||
|
||||
#include "Fastor/util/timeit.h"
|
||||
#include "Fastor/util/print.h"
|
||||
#include "Fastor/util/write.h"
|
||||
#include "Fastor/util/types.h"
|
||||
|
||||
#endif
|
||||
43
noarch/include/Fastor/util/write.h
Normal file
43
noarch/include/Fastor/util/write.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef WRITE_H
|
||||
#define WRITE_H
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace Fastor {
|
||||
|
||||
template<typename T>
|
||||
inline void write(const std::string &filename, const T &a) {
|
||||
// if ( filename.empty() )
|
||||
// filename = "output";
|
||||
std::ofstream outfile;
|
||||
outfile.open(filename,std::ios_base::app);
|
||||
outfile << a << "\n";
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
template<typename T, typename ... Rest>
|
||||
inline void write(const std::string &filename, const T &first, const Rest& ... rest) {
|
||||
write(filename,first);
|
||||
write(filename,rest...);
|
||||
}
|
||||
|
||||
inline void write(const std::string &filename) {
|
||||
std::ofstream outfile;
|
||||
outfile.open(filename,std::ios_base::app);
|
||||
outfile << "\n";
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
//void write() {
|
||||
// const std::string &filename = "output";
|
||||
// std::ofstream outfile;
|
||||
// outfile.open(filename,std::ios_base::app);
|
||||
// outfile << "\n";
|
||||
// outfile.close();
|
||||
//}
|
||||
|
||||
|
||||
} // end of namespace
|
||||
|
||||
#endif // WRITE_H
|
||||
|
||||
Reference in New Issue
Block a user