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)
13 static constexpr const char* UNIX_PROC_STAT { "/proc/stat" };
14 #endif
15 #if defined(__linux__) || defined(Q_OS_ANDROID) || defined(Q_OS_MACOS)
16 static 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 
26 Jitterometer::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 }
Jitterometer::m_numCycles
int m_numCycles
Definition: jitterometer.h:64
Jitterometer::~Jitterometer
~Jitterometer()
Definition: jitterometer.cpp:61
Jitterometer::SetNumCycles
void SetNumCycles(int cycles)
Definition: jitterometer.cpp:69
Jitterometer::m_starttime
std::chrono::microseconds m_starttime
Definition: jitterometer.h:65
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
Jitterometer::RecordEndTime
bool RecordEndTime()
Definition: jitterometer.cpp:85
Jitterometer::Jitterometer
Jitterometer(QString nname, int ncycles=0)
Definition: jitterometer.cpp:26
Jitterometer::GetCPUStat
QString GetCPUStat(void)
Definition: jitterometer.cpp:149
mythlogging.h
MythFile::copy
MBASE_PUBLIC long long copy(QFile &dst, QFile &src, uint block_size=0)
Copies src file to dst file.
Definition: mythmiscutil.cpp:263
Jitterometer::m_lastFps
float m_lastFps
Definition: jitterometer.h:67
Jitterometer::m_times
QVector< std::chrono::microseconds > m_times
Definition: jitterometer.h:66
Jitterometer::m_count
int m_count
Definition: jitterometer.h:63
jitterometer.h
MAX_CORES
static constexpr size_t MAX_CORES
Definition: jitterometer.cpp:16
Jitterometer::m_cpuStat
QFile * m_cpuStat
Definition: jitterometer.h:70
Jitterometer::RecordStartTime
void RecordStartTime()
Definition: jitterometer.cpp:142
Jitterometer::m_name
QString m_name
Definition: jitterometer.h:69
Jitterometer::m_lastSd
float m_lastSd
Definition: jitterometer.h:68
Jitterometer::RecordCycleTime
bool RecordCycleTime()
Definition: jitterometer.cpp:76
std
Definition: mythchrono.h:23
mythchrono.h
Jitterometer::m_lastCpuStats
QString m_lastCpuStats
Definition: jitterometer.h:72
Jitterometer::m_lastStats
unsigned long long * m_lastStats
Definition: jitterometer.h:71