/* PCSX2 - PS2 Emulator for PCs * Copyright (C) 2002-2021 PCSX2 Dev Team * * PCSX2 is free software: you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Found- * ation, either version 3 of the License, or (at your option) any later version. * * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with PCSX2. * If not, see . */ #include "PrecompiledHeader.h" #include "Timer.h" #include #include #if defined(_WIN32) #include "RedtapeWindows.h" #elif defined(__APPLE__) #include #include #include #else #include #include #include #include #endif namespace Common { #ifdef _WIN32 static double s_counter_frequency; static bool s_counter_initialized = false; Timer::Value Timer::GetCurrentValue() { // even if this races, it should still result in the same value.. if (!s_counter_initialized) { LARGE_INTEGER Freq; QueryPerformanceFrequency(&Freq); s_counter_frequency = static_cast(Freq.QuadPart) / 1000000000.0; s_counter_initialized = true; } Timer::Value ReturnValue; QueryPerformanceCounter(reinterpret_cast(&ReturnValue)); return ReturnValue; } double Timer::ConvertValueToNanoseconds(Timer::Value value) { return (static_cast(value) / s_counter_frequency); } double Timer::ConvertValueToMilliseconds(Timer::Value value) { return ((static_cast(value) / s_counter_frequency) / 1000000.0); } double Timer::ConvertValueToSeconds(Timer::Value value) { return ((static_cast(value) / s_counter_frequency) / 1000000000.0); } Timer::Value Timer::ConvertSecondsToValue(double s) { return static_cast((s * 1000000000.0) * s_counter_frequency); } Timer::Value Timer::ConvertMillisecondsToValue(double ms) { return static_cast((ms * 1000000.0) * s_counter_frequency); } Timer::Value Timer::ConvertNanosecondsToValue(double ns) { return static_cast(ns * s_counter_frequency); } #else Timer::Value Timer::GetCurrentValue() { struct timespec tv; clock_gettime(CLOCK_MONOTONIC, &tv); return ((Value)tv.tv_nsec + (Value)tv.tv_sec * 1000000000); } double Timer::ConvertValueToNanoseconds(Timer::Value value) { return static_cast(value); } double Timer::ConvertValueToMilliseconds(Timer::Value value) { return (static_cast(value) / 1000000.0); } double Timer::ConvertValueToSeconds(Timer::Value value) { return (static_cast(value) / 1000000000.0); } Timer::Value Timer::ConvertSecondsToValue(double s) { return static_cast(s * 1000000000.0); } Timer::Value Timer::ConvertMillisecondsToValue(double ms) { return static_cast(ms * 1000000.0); } Timer::Value Timer::ConvertNanosecondsToValue(double ns) { return static_cast(ns); } #endif Timer::Timer() { Reset(); } void Timer::Reset() { m_tvStartValue = GetCurrentValue(); } double Timer::GetTimeSeconds() const { return ConvertValueToSeconds(GetCurrentValue() - m_tvStartValue); } double Timer::GetTimeMilliseconds() const { return ConvertValueToMilliseconds(GetCurrentValue() - m_tvStartValue); } double Timer::GetTimeNanoseconds() const { return ConvertValueToNanoseconds(GetCurrentValue() - m_tvStartValue); } double Timer::GetTimeSecondsAndReset() { const Value value = GetCurrentValue(); const double ret = ConvertValueToSeconds(value - m_tvStartValue); m_tvStartValue = value; return ret; } double Timer::GetTimeMillisecondsAndReset() { const Value value = GetCurrentValue(); const double ret = ConvertValueToMilliseconds(value - m_tvStartValue); m_tvStartValue = value; return ret; } double Timer::GetTimeNanosecondsAndReset() { const Value value = GetCurrentValue(); const double ret = ConvertValueToNanoseconds(value - m_tvStartValue); m_tvStartValue = value; return ret; } ThreadCPUTimer::ThreadCPUTimer() = default; ThreadCPUTimer::ThreadCPUTimer(ThreadCPUTimer&& move) : m_thread_handle(move.m_thread_handle) { move.m_thread_handle = nullptr; } ThreadCPUTimer::~ThreadCPUTimer() { #ifdef _WIN32 CloseHandle(reinterpret_cast(m_thread_handle)); #endif } ThreadCPUTimer& ThreadCPUTimer::operator=(ThreadCPUTimer&& move) { std::swap(m_thread_handle, move.m_thread_handle); return *this; } void ThreadCPUTimer::Reset() { m_start_value = GetCurrentValue(); } ThreadCPUTimer::Value ThreadCPUTimer::GetCurrentValue() const { #if defined(_WIN32) FILETIME create, exit, user, kernel; if (!m_thread_handle || !GetThreadTimes((HANDLE)m_thread_handle, &create, &exit, &user, &kernel)) return 0; Value value = (static_cast(user.dwHighDateTime) << 32) | (static_cast(user.dwLowDateTime)); value += (static_cast(kernel.dwHighDateTime) << 32) | (static_cast(kernel.dwLowDateTime)); return value; #elif defined(__APPLE__) thread_basic_info_data_t info; mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; const kern_return_t kr = thread_info((mach_port_t) reinterpret_cast(m_thread_handle), THREAD_BASIC_INFO, (thread_info_t)&info, &count); if (kr != KERN_SUCCESS) return 0; Value value = (static_cast(info.user_time.seconds) * 1000000) + (static_cast(info.user_time.microseconds)); value += (static_cast(info.system_time.seconds) * 1000000) + (static_cast(info.system_time.microseconds)); return value; #else clockid_t cid; if (!m_thread_handle || pthread_getcpuclockid((pthread_t)m_thread_handle, &cid) != 0) return 0; struct timespec ts; if (clock_gettime(cid, &ts) != 0) return 0; return (static_cast(ts.tv_nsec) + static_cast(ts.tv_sec) * 1000000000LL); #endif } double ThreadCPUTimer::GetTimeSeconds() const { return ConvertValueToSeconds(GetCurrentValue() - m_start_value); } double ThreadCPUTimer::GetTimeMilliseconds() const { return ConvertValueToMilliseconds(GetCurrentValue() - m_start_value); } double ThreadCPUTimer::GetTimeNanoseconds() const { return ConvertValueToNanoseconds(GetCurrentValue() - m_start_value); } void ThreadCPUTimer::GetUsageInSecondsAndReset(Value time_diff, double* usage_time, double* usage_percent) { const Value new_value = GetCurrentValue(); const Value diff = new_value - m_start_value; m_start_value = new_value; *usage_time = ConvertValueToSeconds(diff); *usage_percent = GetUtilizationPercentage(time_diff, diff); } void ThreadCPUTimer::GetUsageInMillisecondsAndReset(Value time_diff, double* usage_time, double* usage_percent) { const Value new_value = GetCurrentValue(); const Value diff = new_value - m_start_value; m_start_value = new_value; *usage_time = ConvertValueToMilliseconds(diff); *usage_percent = GetUtilizationPercentage(time_diff, diff); } void ThreadCPUTimer::GetUsageInNanosecondsAndReset(Value time_diff, double* usage_time, double* usage_percent) { const Value new_value = GetCurrentValue(); const Value diff = new_value - m_start_value; m_start_value = new_value; *usage_time = ConvertValueToNanoseconds(diff); *usage_percent = GetUtilizationPercentage(time_diff, diff); } double ThreadCPUTimer::GetUtilizationPercentage(Timer::Value time_diff, Value cpu_time_diff) { #if defined(_WIN32) return ((static_cast(cpu_time_diff) * 10000.0) / (static_cast(time_diff) / s_counter_frequency)); #elif defined(__APPLE__) // microseconds, but time_tiff is in nanoseconds, so multiply by 1000 * 100 return (static_cast(cpu_time_diff) * 100000.0) / static_cast(time_diff); #else // nanoseconds return (static_cast(cpu_time_diff) * 100.0) / static_cast(time_diff); #endif } double ThreadCPUTimer::ConvertValueToSeconds(Value value) { #if defined(_WIN32) // 100ns units return (static_cast(value) / 10000000.0); #elif defined(__APPLE__) // microseconds return (static_cast(value) / 1000000.0); #else // nanoseconds return (static_cast(value) / 1000000000.0); #endif } double ThreadCPUTimer::ConvertValueToMilliseconds(Value value) { #if defined(_WIN32) return (static_cast(value) / 10000.0); #elif defined(__APPLE__) return (static_cast(value) / 1000.0); #else return (static_cast(value) / 1000000.0); #endif } double ThreadCPUTimer::ConvertValueToNanoseconds(Value value) { #if defined(_WIN32) return (static_cast(value) * 100.0); #elif defined(__APPLE__) return (static_cast(value) * 1000.0); #else return static_cast(value); #endif } ThreadCPUTimer ThreadCPUTimer::GetForCallingThread() { ThreadCPUTimer ret; #if defined(_WIN32) ret.m_thread_handle = (void*)OpenThread(THREAD_QUERY_INFORMATION, FALSE, GetCurrentThreadId()); #elif defined(__APPLE__) ret.m_thread_handle = reinterpret_cast((uintptr_t)mach_thread_self()); #else ret.m_thread_handle = (void*)pthread_self(); #endif ret.Reset(); return ret; } } // namespace Common