#ifdef GEN_INTELLISENSE_DIRECTIVES # pragma once # include "filesystem.cpp" #endif #pragma region Timing GEN_API_C_BEGIN #ifdef GEN_BENCHMARK #if defined( GEN_COMPILER_MSVC ) && ! defined( __clang__ ) u64 read_cpu_time_stamp_counter( void ) { return __rdtsc(); } #elif defined( __i386__ ) u64 read_cpu_time_stamp_counter( void ) { u64 x; __asm__ volatile( ".byte 0x0f, 0x31" : "=A"( x ) ); return x; } #elif defined( __x86_64__ ) u64 read_cpu_time_stamp_counter( void ) { u32 hi, lo; __asm__ __volatile__( "rdtsc" : "=a"( lo ), "=d"( hi ) ); return scast( u64, lo ) | ( scast( u64, hi ) << 32 ); } #elif defined( __powerpc__ ) u64 read_cpu_time_stamp_counter( void ) { u64 result = 0; u32 upper, lower, tmp; __asm__ volatile( "0: \n" "\tmftbu %0 \n" "\tmftb %1 \n" "\tmftbu %2 \n" "\tcmpw %2,%0 \n" "\tbne 0b \n" : "=r"( upper ), "=r"( lower ), "=r"( tmp ) ); result = upper; result = result << 32; result = result | lower; return result; } #elif defined( GEN_SYSTEM_EMSCRIPTEN ) u64 read_cpu_time_stamp_counter( void ) { return ( u64 )( emscripten_get_now() * 1e+6 ); } #elif defined( GEN_CPU_ARM ) && ! defined( GEN_COMPILER_TINYC ) u64 read_cpu_time_stamp_counter( void ) { # if defined( __aarch64__ ) int64_t r = 0; asm volatile( "mrs %0, cntvct_el0" : "=r"( r ) ); # elif ( __ARM_ARCH >= 6 ) uint32_t r = 0; uint32_t pmccntr; uint32_t pmuseren; uint32_t pmcntenset; // Read the user mode perf monitor counter access permissions. asm volatile( "mrc p15, 0, %0, c9, c14, 0" : "=r"( pmuseren ) ); if ( pmuseren & 1 ) { // Allows reading perfmon counters for user mode code. asm volatile( "mrc p15, 0, %0, c9, c12, 1" : "=r"( pmcntenset ) ); if ( pmcntenset & 0x80000000ul ) { // Is it counting? asm volatile( "mrc p15, 0, %0, c9, c13, 0" : "=r"( pmccntr ) ); // The counter is set up to count every 64th cycle return ( ( int64_t )pmccntr ) * 64; // Should optimize to << 6 } } # else # error "No suitable method for read_cpu_time_stamp_counter for this cpu type" # endif return r; } #else u64 read_cpu_time_stamp_counter( void ) { GEN_PANIC( "read_cpu_time_stamp_counter is not supported on this particular setup" ); return -0; } #endif #if defined( GEN_SYSTEM_WINDOWS ) || defined( GEN_SYSTEM_CYGWIN ) u64 time_rel_ms( void ) { local_persist LARGE_INTEGER win32_perf_count_freq = {}; u64 result; LARGE_INTEGER counter; local_persist LARGE_INTEGER win32_perf_counter = {}; if ( ! win32_perf_count_freq.QuadPart ) { QueryPerformanceFrequency( &win32_perf_count_freq ); GEN_ASSERT( win32_perf_count_freq.QuadPart != 0 ); QueryPerformanceCounter( &win32_perf_counter ); } QueryPerformanceCounter( &counter ); result = ( counter.QuadPart - win32_perf_counter.QuadPart ) * 1000 / ( win32_perf_count_freq.QuadPart ); return result; } #else # if defined( GEN_SYSTEM_LINUX ) || defined( GEN_SYSTEM_FREEBSD ) || defined( GEN_SYSTEM_OPENBSD ) || defined( GEN_SYSTEM_EMSCRIPTEN ) u64 _unix_gettime( void ) { struct timespec t; u64 result; clock_gettime( 1 /*CLOCK_MONOTONIC*/, &t ); result = 1000 * t.tv_sec + 1.0e-6 * t.tv_nsec; return result; } # endif u64 time_rel_ms( void ) { # if defined( GEN_SYSTEM_OSX ) u64 result; local_persist u64 timebase = 0; local_persist u64 timestart = 0; if ( ! timestart ) { mach_timebase_info_data_t tb = { 0 }; mach_timebase_info( &tb ); timebase = tb.numer; timebase /= tb.denom; timestart = mach_absolute_time(); } // NOTE: mach_absolute_time() returns things in nanoseconds result = 1.0e-6 * ( mach_absolute_time() - timestart ) * timebase; return result; # else local_persist u64 unix_timestart = 0.0; if ( ! unix_timestart ) { unix_timestart = _unix_gettime(); } u64 now = _unix_gettime(); return ( now - unix_timestart ); # endif } #endif f64 time_rel( void ) { return ( f64 )( time_rel_ms() * 1e-3 ); } #endif GEN_API_C_END #pragma endregion Timing