#include <string>
#include <iostream>
#include <system_error>
#include <cerrno>
#include <signal.h>

#include <cstdint>

uint64_t rdtsc();

#if defined(__i386) || defined(__x86_64__) // gcc,clang
inline uint64_t rdtsc()
{
    uint32_t tickl, tickh;
    asm volatile("rdtsc":"=a"(tickl),"=d"(tickh));
    return ( static_cast<uint64_t>(tickh) << 32) | tickl;
}
#elif defined(_M_IX86) || defined(_M_X64) // MSVC
#include <intrin.h>
#pragma intrinsic(__rdtsc)

inline uint64_t rdtsc()
{
    return __rdtsc();
}
#endif


template<typename F>
void bench(std::string name,
           F func)
{
    std::cout<<"Benchmarking \"" << name << "\"\n";
    uint64_t tsc_begin, second_run, first_run;
    // first run
    tsc_begin = rdtsc();
    func();
    first_run = rdtsc() - tsc_begin;
    // second run
    tsc_begin = rdtsc();
    func();
    second_run = rdtsc() - tsc_begin;
    std::cout << "tsc: " << first_run << " " << second_run << "\n";
}

int failing_func(){
    sigset_t sig;
    return sigprocmask(-1, &sig, nullptr);
}

int success_func(){
    return sigprocmask(0, nullptr, nullptr);
}

void with_exception_oftenfail(){
    int res = failing_func();
    if(res == -1) throw std::system_error(errno, std::system_category());
}

int with_error_code_oftenfail(){
    return failing_func();
}

void with_exception_rarefail(){
    int res = success_func();
    if(res == -1) throw std::system_error(errno, std::system_category());
}

int with_error_code_rarefail(){
    return success_func();
}

int main(){
    bench( "rdtsc()", [](){} );

    unsigned int black_hole = 0;
    bench("Exceptions (often error)", [&black_hole](){
        try{
            with_exception_oftenfail();
        } catch(std::exception&){
            black_hole++;
        }
    });

    bench("Error codes (often error)", [&black_hole](){
        int res = with_error_code_oftenfail();
        if(res == -1) black_hole--;
    });

    bench("Exceptions (rare error)", [&black_hole](){
        try{
            with_exception_rarefail();
        } catch(std::exception&){
            black_hole++;
        }
    });

    bench("Error codes (rare error)", [&black_hole](){
        int res = with_error_code_rarefail();
        if(res == -1) black_hole--;
    });

    //To prevent loop optimize-out
    std::cout<<"Blackhole is:"<<black_hole<<std::endl;
}
