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