#include "Metrics.h" #include "StringifiedEnum.h" #include namespace { std::unordered_map g_Metrics = {}; std::vector g_Variables = { MetricVariable::GameLoop, MetricVariable::PacketHandling, MetricVariable::UpdateEntities, MetricVariable::UpdateSpawners, MetricVariable::Physics, MetricVariable::UpdateReplica, MetricVariable::Ghosting, MetricVariable::CPUTime, MetricVariable::Sleep, MetricVariable::Frame, }; } void Metrics::AddMeasurement(MetricVariable variable, int64_t value) { auto& metric = g_Metrics[variable]; AddMeasurement(metric, value); } void Metrics::AddMeasurement(Metric& metric, int64_t value) { const auto index = metric.measurementIndex; metric.measurements[index] = value; if (metric.max == -1 || value > metric.max) { metric.max = value; } else if (metric.min == -1 || metric.min > value) { metric.min = value; } if (metric.measurementSize < MAX_MEASURMENT_POINTS) { metric.measurementSize++; } metric.measurementIndex = (index + 1) % MAX_MEASURMENT_POINTS; } const Metric& Metrics::GetMetric(MetricVariable variable) { auto& metric = g_Metrics[variable]; int64_t average = 0; for (size_t i = 0; i < metric.measurementSize; i++) { average += metric.measurements[i]; } average /= metric.measurementSize; metric.average = average; return metric; } void Metrics::StartMeasurement(MetricVariable variable) { auto& metric = g_Metrics[variable]; metric.activeMeasurement = std::chrono::high_resolution_clock::now(); } void Metrics::EndMeasurement(MetricVariable variable) { const auto end = std::chrono::high_resolution_clock::now(); auto& metric = g_Metrics[variable]; const auto elapsed = end - metric.activeMeasurement; const auto nanoseconds = std::chrono::duration_cast(elapsed).count(); AddMeasurement(metric, nanoseconds); } float Metrics::ToMiliseconds(int64_t nanoseconds) { return static_cast(nanoseconds) / 1e6; } const std::string_view Metrics::MetricVariableToString(MetricVariable variable) { return StringifiedEnum::ToString(variable); } const std::vector& Metrics::GetAllMetrics() { return g_Variables; } /* RSS Memory utilities * * Author: David Robert Nadeau * Site: http://NadeauSoftware.com/ * License: Creative Commons Attribution 3.0 Unported License * http://creativecommons.org/licenses/by/3.0/deed.en_US */ #if defined(_WIN32) #include #include #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) #include #include #if defined(__APPLE__) && defined(__MACH__) #include #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) #include #include #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) #include #endif #else #error "Cannot define getPeakRSS( ) or getCurrentRSS( ) for an unknown OS." #endif /** * Returns the peak (maximum so far) resident set size (physical * memory use) measured in bytes, or zero if the value cannot be * determined on this OS. */ size_t Metrics::GetPeakRSS() { #if defined(_WIN32) /* Windows -------------------------------------------------- */ PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); return static_cast(info.PeakWorkingSetSize); #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) /* AIX and Solaris ------------------------------------------ */ struct psinfo psinfo; int fd = -1; if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) return static_cast(0L); /* Can't open? */ if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { close(fd); return static_cast(0L); /* Can't read? */ } close(fd); return static_cast(psinfo.pr_rssize * 1024L); #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) /* BSD, Linux, and OSX -------------------------------------- */ struct rusage rusage; getrusage(RUSAGE_SELF, &rusage); #if defined(__APPLE__) && defined(__MACH__) return static_cast(rusage.ru_maxrss); #else return static_cast(rusage.ru_maxrss * 1024L); #endif #else /* Unknown OS ----------------------------------------------- */ return static_cast(0L); /* Unsupported. */ #endif } /** * Returns the current resident set size (physical memory use) measured * in bytes, or zero if the value cannot be determined on this OS. */ size_t Metrics::GetCurrentRSS() { #if defined(_WIN32) /* Windows -------------------------------------------------- */ PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); return static_cast(info.WorkingSetSize); #elif defined(__APPLE__) && defined(__MACH__) /* OSX ------------------------------------------------------ */ struct mach_task_basic_info info; mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, reinterpret_cast(&info), &infoCount) != KERN_SUCCESS) return static_cast(0L); /* Can't access? */ return static_cast(info.resident_size); #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) /* Linux ---------------------------------------------------- */ long rss = 0L; FILE* fp = NULL; if ((fp = fopen("/proc/self/statm", "r")) == NULL) return static_cast(0L); /* Can't open? */ if (fscanf(fp, "%*s%ld", &rss) != 1) { fclose(fp); return static_cast(0L); /* Can't read? */ } fclose(fp); return static_cast(rss) * static_cast(sysconf(_SC_PAGESIZE)); #else /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ return static_cast(0L); /* Unsupported. */ #endif } size_t Metrics::GetProcessID() { #if defined(_WIN32) return GetCurrentProcessId(); #else return getpid(); #endif }