MythTV master
jitterometer.cpp
Go to the documentation of this file.
1// MythTV
4#include "jitterometer.h"
5
6// Std
7#include <algorithm>
8#include <array>
9#include <cmath>
10#include <cstdlib>
11#include <utility>
12
13#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
14static constexpr const char* UNIX_PROC_STAT { "/proc/stat" };
15#endif
16#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID) || defined(Q_OS_MACOS)
17static constexpr size_t MAX_CORES { 8 };
18#endif
19
20#ifdef Q_OS_MACOS
21#include <mach/mach_init.h>
22#include <mach/mach_error.h>
23#include <mach/mach_host.h>
24#include <mach/vm_map.h>
25#endif
26
27Jitterometer::Jitterometer(QString nname, int ncycles)
28 : m_numCycles(ncycles), m_name(std::move(nname))
29{
30 m_times.resize(m_numCycles);
31
32 if (m_name.isEmpty())
33 m_name = "Jitterometer";
34
35#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
36 // N.B. Access to /proc/stat was revoked on Android for API >=26 (Oreo)
37 if (QFile::exists(UNIX_PROC_STAT))
38 {
39 m_cpuStat = new QFile(UNIX_PROC_STAT);
40 if (m_cpuStat)
41 {
42 if (!m_cpuStat->open(QIODevice::ReadOnly))
43 {
44 delete m_cpuStat;
45 m_cpuStat = nullptr;
46 }
47 else
48 {
49 m_lastStats = new unsigned long long[MAX_CORES * 9];
50 memset(m_lastStats, 0, sizeof(unsigned long long) * MAX_CORES * 9);
51 }
52 }
53 }
54#endif
55
56#ifdef Q_OS_MACOS
57 m_lastStats = new unsigned long long[MAX_CORES * 2];
58 memset(m_lastStats, 0, sizeof(unsigned long long) * MAX_CORES * 2);
59#endif
60}
61
63{
64 if (m_cpuStat)
65 m_cpuStat->close();
66 delete m_cpuStat;
67 delete [] m_lastStats;
68}
69
71{
72 m_numCycles = cycles;
73 m_times.resize(m_numCycles);
74 m_count = 0;
75}
76
78{
79 if (!m_numCycles)
80 return false;
81 bool ret = RecordEndTime();
83 return ret;
84}
85
87{
88 if (!m_numCycles)
89 return false;
90
91 int cycles = m_numCycles;
92 auto timenow = nowAsDuration<std::chrono::microseconds>();
93
94 if (m_starttime > 0ms)
95 {
96 m_times[m_count] = timenow - m_starttime;
97 m_count++;
98 }
99
100 m_starttime = -1us;
101
102 if (m_count >= cycles)
103 {
104 /* compute and display stuff, reset count to -1 */
105
106 /* compute the mean */
107 std::chrono::microseconds tottime = std::accumulate(m_times.cbegin(), m_times.cend(), 0us);
108 using doublemics = std::chrono::duration<double, std::micro>;
109 doublemics mean = duration_cast<doublemics>(tottime) / cycles;
110
111 if (tottime > 0us)
112 m_lastFps = cycles * (1000000.0F / tottime.count());
113
114 /* compute the sum of the squares of each deviation from the mean */
115 double sum_of_squared_deviations =
116 std::accumulate(m_times.cbegin(), m_times.cend(), 0.0,
117 [mean](double sum, std::chrono::microseconds time)
118 {double delta = (mean - duration_cast<doublemics>(time)).count();
119 return sum + (delta * delta); });
120
121 /* compute standard deviation */
122 double standard_deviation = sqrt(sum_of_squared_deviations / (cycles - 1));
123 if (mean > 0us)
124 m_lastSd = standard_deviation / mean.count();
125
126 /* retrieve load if available */
127 QString extra;
129 if (!m_lastCpuStats.isEmpty())
130 extra = QString("CPUs: ") + m_lastCpuStats;
131
132 LOG(VB_GENERAL, LOG_INFO,
133 m_name + QString("FPS: %1 Mean: %2 Std.Dev: %3 ")
134 .arg(m_lastFps, 7, 'f', 2).arg((int)mean.count(), 5)
135 .arg((int)standard_deviation, 5) + extra);
136
137 m_count = 0;
138 return true;
139 }
140 return false;
141}
142
144{
145 if (!m_numCycles)
146 return;
147 m_starttime = nowAsDuration<std::chrono::microseconds>();
148}
149
151{
152 QString result = "N/A";
153
154#if defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)
155 if (m_cpuStat)
156 {
157 m_cpuStat->seek(0);
158 m_cpuStat->flush();
159
160 QByteArray line = m_cpuStat->readLine(256);
161 if (line.isEmpty())
162 return result;
163
164 result = "";
165 size_t cores = 0;
166 int ptr = 0;
167 line = m_cpuStat->readLine(256);
168 while (!line.isEmpty() && cores < MAX_CORES)
169 {
170 std::array<unsigned long long,9> stats {};
171 int num = 0;
172 if (sscanf(line.constData(),
173 "cpu%30d %30llu %30llu %30llu %30llu %30llu "
174 "%30llu %30llu %30llu %30llu %*5000s\n",
175 &num, stats.data(), &stats[1], &stats[2], &stats[3],
176 &stats[4], &stats[5], &stats[6], &stats[7], &stats[8]) >= 4)
177 {
178 float load = stats[0] + stats[1] + stats[2] + stats[4] +
179 stats[5] + stats[6] + stats[7] + stats[8] -
180 m_lastStats[ptr + 0] - m_lastStats[ptr + 1] -
181 m_lastStats[ptr + 2] - m_lastStats[ptr + 4] -
182 m_lastStats[ptr + 5] - m_lastStats[ptr + 6] -
183 m_lastStats[ptr + 7] - m_lastStats[ptr + 8];
184 float total = load + stats[3] - m_lastStats[ptr + 3];
185 if (total > 0)
186 result += QString("%1% ").arg(load / total * 100, 0, 'f', 0);
187 std::ranges::copy(stats, &m_lastStats[ptr]);
188 }
189 line = m_cpuStat->readLine(256);
190 cores++;
191 ptr += 9;
192 }
193 }
194#endif
195
196#ifdef Q_OS_MACOS
197 processor_cpu_load_info_t load = nullptr;
198 mach_msg_type_number_t msgcount = 0;
199 natural_t processorcount = 0;
200
201 if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &processorcount, (processor_info_array_t *)&load, &msgcount) == KERN_SUCCESS)
202 {
203 result = "";
204 int ptr = 0;
205 for (natural_t i = 0; i < processorcount && i < MAX_CORES; i++)
206 {
207 std::array<unsigned long long,2> stats {};
208 stats[0] = load[i].cpu_ticks[CPU_STATE_IDLE];
209 stats[1] = load[i].cpu_ticks[CPU_STATE_IDLE] + load[i].cpu_ticks[CPU_STATE_USER] +
210 load[i].cpu_ticks[CPU_STATE_SYSTEM] + load[i].cpu_ticks[CPU_STATE_NICE];
211 double idledelta = stats[0] - m_lastStats[ptr];
212 double totaldelta = stats[1] - m_lastStats[ptr + 1];
213 if (totaldelta > 0)
214 result += QString("%1% ").arg(((totaldelta - idledelta) / totaldelta) * 100.0, 0, 'f', 0);
215 std::ranges::copy(stats, &m_lastStats[ptr]);
216 ptr += 2;
217 }
218 }
219#endif
220 return result;
221}
QString m_name
Definition: jitterometer.h:69
Jitterometer(QString nname, int ncycles=0)
void RecordStartTime()
QString m_lastCpuStats
Definition: jitterometer.h:72
QFile * m_cpuStat
Definition: jitterometer.h:70
QVector< std::chrono::microseconds > m_times
Definition: jitterometer.h:66
bool RecordEndTime()
bool RecordCycleTime()
void SetNumCycles(int cycles)
float m_lastSd
Definition: jitterometer.h:68
std::chrono::microseconds m_starttime
Definition: jitterometer.h:65
QString GetCPUStat(void)
float m_lastFps
Definition: jitterometer.h:67
unsigned long long * m_lastStats
Definition: jitterometer.h:71
static constexpr size_t MAX_CORES
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
bool exists(str path)
Definition: xbmcvfs.py:51